Showing posts with label ABAP OOPS. Show all posts
Showing posts with label ABAP OOPS. Show all posts

Monday, October 13, 2014

OO Programming with ABAP Objects: Interfaces

Throughout the course of this blog series, we have covered the basic cornerstones of object-oriented programming including:



In this final installment of the series, we will expand upon the concept of inheritance/polymorphism by introducing you to the concept of interfaces.


Why do we need interfaces?


In my previous OO Programming with ABAP Objects: Polymorphism, we explored the notion of interface inheritance and showed you how to use it to implement polymorphic designs. Here, the basic premise is that since a subclass inherits the public interface of its superclass, you can invoke methods on an instance of a subclass in exactly the same way that you call them using an instance of the superclass. As we learned, you can leverage this functionality to develop generic methods that can work with an instance of the superclass or any of its subclasses. This is all fine and well when you're working with classes that fit neatly into a particular inheritance model. But what happens when you want to plug in functionality from a class that already has an inheritance relationship defined?

In some programming languages, it is possible to define multiple inheritance relationships in which a given class can inherit from several parent classes. For instance, in the UML class diagram below, class D inherits from classes B and C. Though the concept of multiple inheritance may sound appealing on a conceptual level, it can cause some serious problems on an implementation level. Looking at the UML class diagram below, consider the inheritance of method "someMethod()" for class D. Here, let's assume that classes B and C have overridden the default implementation of this method from class A. Based on this, which implementation of method "someMethod()" does class D inherit: the one from class B or class C? In object-oriented programming parlance, this conundrum is referred to as the diamond problem.

Diamond Problem

Rather than try and tackle multiple inheritance issues such as the diamond problem, the designers of the ABAP Objects language elected to adopt a single inheritance model. This implies that a class can only inherit from a single parent class. This does not mean, however, that you cannot implement multiple inheritance in ABAP. Rather, you simply must go about defining it in a different way using interfaces.

In order to explain the concept of interfaces, it is helpful to see an example of how they are used in code. Consider the LIF_COMPARABLE interface shown below. This interface defines a single method called "compare_to()" that returns an integer indicating whether or not an object is less than, greater than, or equal to another object of the same type. As you can see, we have only defined the method here; there is no implementation provided. Indeed, you are not even allowed to provide implementations within an interface definition.

INTERFACE lif_comparable.
METHODS:
compare_to IMPORTING im_object TYPE REF TO object,
RETURNING VALUE(re_result) TYPE i.
ENDINTERFACE.

Looking at the definition of the LIF_COMPARABLE interface above, you might be wondering why we would want to bother defining an interface. After all, they don't anything particularly exciting. Still, much like classes, it does encapsulate a unique concept: comparability. Comparability is a feature that we would like to implement in a number of different classes. In fact, defining comparability in a common interface enables us to develop generic algorithms for sorting objects, etc. The question is how. Since many of the classes we want to implement this with likely have pre-existing inheritance relationships, we can't define the comparison functionality in a common superclass. However, we can model this functionality in an interface.

Taking our comparability example a step further, let's imagine that we want to define a sort order for a set of customer objects of type LCL_CUSTOMER. For the purposes of our discussion, let's assume that class LCL_CUSTOMER inherits from a base business partner class called LCL_PARTNER. In order to assume the comparability feature, LCL_CUSTOMER also implements the LIF_COMPARABLE interface as shown below.

CLASS lcl_customer DEFINITION
INHERITING FROM lcl_partner.
PUBLIC SECTION.
INTERFACES: lif_comparable.
"Other declarations here...
ENDCLASS.

CLASS lcl_customer IMPLEMENTATION.
METHOD lif_comparable~compare_to.
"Implement comparison logic here...
ENDMETHOD.
ENDCLASS.
Looking at the code excerpt above, you can see that we have split class LCL_CUSTOMER into two dimensions: a customer is a partner; but it is also comparable. This means that we can substitute instances of class LCL_CUSTOMER anywhere that the interface type LIF_COMPARABLE is used.

Now that you have a feel for how interfaces are used, let's attempt to define interfaces a little more formally. An interface is an abstraction that defines a model (or prototype) of a particular entity or concept. As you saw above, you don't define any kind of implementation for an interface; that is left up to implementing classes. Once a class implements an interface, it fulfills the requirements of an inheritance relationship with the interface. In this way, you can implement multiple inheritance using interfaces. Indeed, classes are free to implement as many interfaces as they wish.


How can I use interfaces in my own designs?


Hopefully by now you can see the power of interfaces, but you may be unsure of how to use them in your own designs. In these early stages of development, it is helpful to look around and see how others are making use of interfaces. In particular, we can look to see how SAP uses interfaces in various development objects. Some of the more common places where interfaces are used extensively by SAP include:



  • The iXML Library used to parse XML in ABAP.

  • The ABAP Object Services framework that enables object-relational persistence models.

  • The Web Dynpro for ABAP (WDA) context API.

  • Business Add-Ins (BAdIs)

  • The Internet Communication Framework (ICF) used to send and receive HTTP request messages.

If you have ever worked with BAdIs before, then perhaps you may have interacted with interfaces without even realizing it. BAdIs are a type of customer enhancement in which customers can implement a "user exit" that supplements core behavior with custom functionality. The screenshot below shows the definition of a BAdI called "CTS_REQUEST_CHECK". This BAdI is used to validate a Change and Transport System (i.e. CTS) transport request at various important milestones. On the Interface tab, notice the interface name "IF_EX_CTS_REQUEST_CHECK". This interface defines the methods "check_before_creation()", etc. shown below. Whenever we create a BAdI implementation for "CTS_REQUEST_CHECK", the system will generate a class that implements this interface behind the scenes. At runtime, the CTS system will invoke these methods polymorphically to implement the desired user exit behavior.

BAdI Definition for CTS_REQUEST_CHECK

The BAdI example above provides us with some useful observations about interfaces:



  1. Interfaces are particularly well suited to modeling behavior. In other words, while classes are often representation of nouns, interfaces can often be used to supplement these core entities with different types of behavior, etc.

  2. Interfaces make it possible to implement polymorphism in a lot of different ways. For example, if a pre-existing class contained functionality to handle CTS request milestones, then we could implement the IF_EX_CTS_REQUEST_CHECK interface in that class rather than reinventing the wheel.

  3. Interfaces allow you to further separate the API interface from its underlying implementation.

Based on these observations, we would offer the following rule of thumb:  when developing your OO designs, try and represent your core API using interfaces. This step helps ensure that your design remains flexible over time. An excellent example of this is the iXML Library used to process XML in ABAP. The only concrete class provided in the iXML Library is the CL_IXML factory class - everything else is interface-driven. This abstraction makes it possible for SAP to neatly swap XML parser implementations behind the scenes without anyone knowing the difference. Similarly, if your core API is represented using interfaces, you have much more flexibility at the implementation layer. Over time, you'll thank yourself for putting in the effort up front.

An excellent resource for coming up to speed with interfaces is the classic software engineering text Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1994). This book allows you to enter the mind of object-oriented pioneers who have documented many useful OO design patterns in an easy-to-read catalog-based format. Digging into these designs, you'll see how interfaces can be used to implement certain types of flexibility that simply cannot be realized with basic inheritance. You'll especially appreciate the ABAP Objects implementation when you see how the authors struggle to implement certain functionality in C++ (which does not support interfaces).


Closing Thoughts and Next Steps


I hope that you have enjoyed this blog series as much as I have enjoyed writing it. Thanks to everyone for their kind and useful feedback; it is much appreciated. In many ways, this series barely scratches the surface of the possibilities of OO programming. If you are interested in learning more, might I offer a shameful plug for my book Object-Oriented Programming with ABAP Objects (SAP Press, 2009). Here, I cover these topics (and more) in much more depth. Best of luck with your object-oriented designs!

Object-Oriented Programming with ABAP Objects

OO Programming with ABAP Objects: Polymorphism

In my previous OO Programming with ABAP Objects: Inheritance, we learned about inheritance relationships. As you may recall, the term inheritance is used to describe a specialization relationship between related classes in a given problem domain. Here, rather than reinvent the wheel, we define a new class in terms of a pre-existing one. The new class (or subclass) is said to inherit from the existing class (or parent class). Most of the time, when people talk about inheritance, they focus their attention on code reuse and the concept of implementation inheritance. Implementation inheritance is all about reducing redundant code by leveraging inherited components to implement new requirements rather than starting all over from scratch.

One aspect of inheritance relationships that sometimes gets swept under the rug is fact that subclasses also inherit the interface of their parent classes. This type of inheritance is described using the term interface inheritance. Interface inheritance makes it possible to use instances of classes in an inheritance hierarchy interchangeably – a concept that is referred to as polymorphism. In this blog, we will explore the idea of polymorphism and show you how to use it to develop highly flexible code.


What is Polymorphism?


As you may recall from my last OO Programming with ABAP Objects: Inheritance, one of the litmus tests for identifying inheritance relationships is to ask yourself whether or not a prospective subclass fits into an “Is-A” relationship with a given parent class. For example, a circle is a type of shape, so defining a “Circle” class in terms of an abstract “Shape” class makes sense. Looking beyond the obvious benefits of reusing any implementation provided in the “Shape” class, let’s think about what this relationship means from an interface perspective. Since the “Circle” class inherits all of the public attributes/methods of the “Shape” class, we can interface with instances of this class in the exact same way that we interface with instances of the “Shape” class. In other words, if the “Shape” class defines a public method called “draw()”, then so does the “Circle” class. Therefore, the code required to call this method on instances of either class is exactly the same even if the underlying implementation is very different.

The term polymorphism literally means “many forms”. From an object-oriented perspective, the term is used to describe a language feature that allows you to use instances of classes belonging to the same inheritance hierarchy interchangeably. This idea is perhaps best explained with an example. Getting back to our “Shape” discussion, let’s think about how we might design a shape drawing program. One possible implementation of the shape drawing program would be to create a class that defines methods like “drawCircle()”, “drawSquare()”, etc. Another approach would be to define a generic method called “draw()” that uses conditional statements to branch the logic out to modules that are used to draw a circle, square, etc. In either case, there is work involved whenever a new shape is added to the mix. Ideally, we would like to decouple the drawing program from our shape hierarchy so that the two can vary independently. We can achieve this kind of design using polymorphism.

In a polymorphic design, we can create a generic method called “draw()” in our drawing program that receives an instance of the “Shape” class as a parameter. Since subclasses of “Shape” inherit its interface, we can pass any kind of shape to the “draw()” method and it can turn around and use that shape instance’s “draw()” method to draw the shape on the screen. In this way, the drawing program is completely ignorant of the type of shape it is handling; it simply delegates the drawing task to the shape instance. This is as it should be since the Shape class already knows how to draw itself. Furthermore, as new shapes are introduced into the mix, no changes would be required to the drawing program so long as these new shapes inherit from the abstract “Shape” class.

This generic approach to programming is often described using the term design by interface. The basic concept here is to adopt a component-based architecture where each component clearly defines the services (i.e. interface) they provide. These interfaces make it easy for components to be weaved together into larger assemblies. Here, notice that we haven’t said anything about how these components are implemented. As long as the components implement the services described in their interface – it really doesn’t matter how they are implemented. From an object-oriented perspective, this means that we can swap out a given object for another so long as they share the same interface. Of course, in order to do so, we need to be able to perform type casts and dynamic method calls.


Type Casting and Dynamic Binding


Most of the time, whenever we talk about the type of an object reference variable in ABAP Objects, we are talking about its static type. The static type of an object reference variable is the class type used to define the reference variable:

DATA: lr_oref TYPE REF TO zcl_shape.

An object reference variable also has a dynamic type associated with it. The dynamic type of an object reference variable is the type of the current object instance that it refers to. Normally, the static and dynamic type of an object reference variable will be the same. However, it is technically possible for an object reference variable to point to an object that is not an instance of the class type used to define the object reference. For example, notice how we are assigning an instance of the ZCL_CIRCLE subclass to the lr_shape object reference variable (whose static type is the parent class ZCL_SHAPE) in the code excerpt below.

DATA: lr_shape  TYPE REF TO zcl_shape,
lr_circle TYPE REF TO zcl_circle.

CREATE OBJECT lr_shape.
CREATE OBJECT lr_circle.
lr_shape = lr_circle.

This kind of assignment is not possible without a type cast. Of course, you can’t perform a type cast using just any class; the source and target object reference variables must be compatible (e.g., their static types must belong to the same inheritance hierarchy). In the example above, once the assignment is completed, the dynamic type of the lr_shape reference variable will be the ZCL_CIRCLE class. Therefore, at runtime, when a method call such as “lr_shape->draw( )” is performed, the ABAP runtime environment will use the dynamic type information to bind the method call with the implementation provided in the ZCL_CIRCLE class.

The type cast above is classified as a narrowing cast (or upcast) as we are narrowing the access scope of the referenced ZCL_CIRCLE object to the components defined in the ZCL_SHAPE superclass. It is also possible to perform a widening cast (or downcast) like this:

DATA: lr_shape  TYPE REF TO zcl_shape,
lr_circle TYPE REF TO zcl_circle.
CREATE OBJECT lr_shape TYPE zcl_circle.
lr_circle ?= lr_shape.

In this case, we are using the TYPE addition to the CREATE OBJECT statement to create an instance of class ZCL_CIRCLE and assign its reference to the lr_shape object reference variable. Then, we use the casting operator (“?=”) to perform a widening cast when we assign the lr_shape object reference to lr_circle. The casting operator is something of a precaution in many respects as widening casts can be dangerous. For instance, in this contrived example, we know that we are assigning an instance of ZCL_CIRCLE to an object reference variable of that type. On the other hand, if the source object reference were a method parameter, we can’t be sure that this is the case. After all, someone could pass in a square to the method and cause all kinds of problems since class ZCL_CIRCLE may well define circle-specific methods that cannot be executed against an instance of class ZCL_SQUARE.


Implementing Polymorphism in ABAP


Now that you have a feel for how type casting works in ABAP Objects, let’s see how to use it to implement a polymorphic design in ABAP. The example code below defines a simple report called ZPOLYTEST that defines an abstract base class called LCL_ANIMAL and two concrete subclasses: LCL_CAT and LCL_DOG. These classes are used to implement a model of the old “See-n-Say” toys manufactured by Mattel, Inc. This model is realized in the form of a local class called LCL_SEE_AND_SAY. If you have never played with a See-n-Say before, its interface is very simple. In the center of the toy is a wheel with pictures of various animals. When a child can positions a lever next to a given animal, the toy will produce the sound of that animal. In order to make the See-n-Say generic, we define the interface of the “play()” method to receive an instance of class LCL_ANIMAL. However, in the START-OF-SELECTION event, you’ll notice that we create instances of LCL_CAT and LCL_DOG and pass them to the See-n-Say. Here, we didn’t have to perform an explicit type cast as narrowing type casts are performed implicitly in method calls. Furthermore, since the LCL_CAT and LCL_DOG classes inherit the methods “get_type()” and “speak()” from class LCL_ANIMAL, we can use instances of them in the LCL_SEE_AND_SAY generically.

REPORT zpolytest.

CLASS lcl_animal DEFINITION ABSTRACT.
PUBLIC SECTION.
METHODS: get_type ABSTRACT,
speak ABSTRACT.
ENDCLASS.

CLASS lcl_cat DEFINITION
INHERITING FROM lcl_animal.
PUBLIC SECTION.
METHODS: get_type REDEFINITION,
speak REDEFINITION.
ENDCLASS.

CLASS lcl_cat IMPLEMENTATION.
METHOD get_type.
WRITE: 'Cat'.
ENDMETHOD.

METHOD speak.
WRITE: 'Meow'.
ENDMETHOD.
ENDCLASS.

CLASS lcl_dog DEFINITION
INHERITING FROM lcl_animal.
PUBLIC SECTION.
METHODS: get_type REDEFINITION,
speak REDEFINITION.
ENDCLASS.

CLASS lcl_dog IMPLEMENTATION.
METHOD get_type.
WRITE: 'Dog'.
ENDMETHOD.

METHOD speak.
WRITE: 'Bark'.
ENDMETHOD.
ENDCLASS.

CLASS lcl_see_and_say DEFINITION.
PUBLIC SECTION.
CLASS-METHODS:
play IMPORTING im_animal
TYPE REF TO lcl_animal.
ENDCLASS.

CLASS lcl_see_and_say IMPLEMENTATION.
METHOD play.
WRITE: 'The'.
CALL METHOD im_animal->get_type.
WRITE: 'says'.
CALL METHOD im_animal->speak.
ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
DATA: lr_cat TYPE REF TO lcl_cat,
lr_dog TYPE REF TO lcl_dog.

CREATE OBJECT lr_cat.
CREATE OBJECT lr_dog.

CALL METHOD lcl_see_and_say=>play
EXPORTING
im_animal = lr_cat.
NEW-LINE.
CALL METHOD lcl_see_and_say=>play
EXPORTING
im_animal = lr_dog.
As mentioned earlier, one of the primary advantages of using polymorphism in a design like this is that we can easily plug in additional animals without having to change anything in class LCL_SEE_AND_SAY. For instance, if we want to add a pig to the See-n-Say, we just create a class LCL_PIG that inherits from LCL_ANIMAL and then we can start passing instances of this class to the “play()” method of class LCL_SEE_AND_SAY.


Conclusions and Next Steps


Hopefully by now you are starting to see the benefit of implementing object-oriented designs. In many respects, polymorphism represents one of the major payoffs for investing the time to create an object-oriented design. However, as you have seen, polymorphism doesn't happen by accident. In order to get there, you need to pay careful attention to the definition of a class' public interface, make good use of encapsulation techniques, and model your class relationships correctly.

If the concept of polymorphism seems familiar, it could be that you’ve seen examples of this in other areas of SAP. Perhaps the most obvious example here would be with “Business Add-Ins” (or BAdIs). In my next blog, we will look at how BAdIs use interfaces to implement polymorphic designs. Interfaces are an important part of any object-oriented developer’s toolbag; making it possible to extend a class into different dimensions

OO Programming with ABAP Objects: Inheritance

In my OO Programming with ABAP Objects: Encapsulation blog entry, we continued our discussion on OOP by showing how visibility sections could be used to hide implementation details of a class. If you are new to OOP, you might be wondering why you would want to go to such lengths to encapsulate your code. After all, don't we want our software to be open these days? However, the use of implementation hiding techniques does not imply that software must be closed off completely. Rather, we just want to establish some healthy boundaries so that we can give the software some structure. This structure helps the software to gracefully adapt to inevitable changes within a particular area without affecting the overall integrity of the software as a whole.

In this blog entry, I will introduce another core concept of OOP called inheritance. Inheritance describes a relationship between related classes within a particular problem domain. Here, you will see that the use of good encapsulation techniques enables you to expand and enhance the functionality of your applications without having to modify pre-existing classes. In my next blog entry, we will see how these relationships can be exploited using polymorphism.


Generalization and Specialization


During the Object-Oriented Analysis & Design (or OOAD) process, we evaulate real world phenomena and try to simulate the problem domain using classes of objects. Frequently, this classification process goes through several iterations before we get it right. For instance, our first pass through the requirements might generate an OO design with some very basic classes. As we dig deeper, we focus in on determining the roles and responsibilities of each class. Along the way, our classes evolve to become more specialized.

In an ideal world, this analysis process would take place in a vaccuum, allowing us to completely refine our object model before we implement it. Unfortunately, most of us do not have this luxury as we are subject to tight deadlines and limited budgets. Typically, we must draw a line in the sand and build the best software we can given the constraints laid before us. In the past, such hasty development has made it very difficult to adapt the software to implement new functionality, etc. Here, developers would have to decide whether or not they felt like an enhancement could be implemented without jeopardizing the existing production code. If the answer to that question was no, they were forced to cut their losses and try to salvage as much of the code as they could using the "copy-and-paste" approach to building new development objects. Both of these approaches are fraught with risks. Early object-oriented researches recognized that there had to be a better way to extending software.

When you think about it, an enhancement extends or specializes a portion of the system in some way. In an OO system, this implies that we want to enhance or extend certain functionality within one or more classes. Here, we don't really want to modify the existing class(es). Rather, we just want to expand then to handle more specialized requirements, etc. One way to implement this kind of specialization in object-oriented languages is through inheritance.

Inheritance defines a relationship between two classes; the original class is called the superclass (or parent class) and the extended class is called the subclass (or child class). In an inheritance relationship, a subclass inherits the components from its superclass (e.g. attributes, methods, etc.). Subclasses can then build on these existing components to implement additional functionality. When thinking about inheritance, it is important to realize that the relationship is not transient in nature. In other words, a subclass is not just a copy or clone of its superclass. For instance, if you change the functionality in a method of a superclass, that change is reflected in its subclasses (except in the case of overridden methods - more on these in a moment). However, the converse is not true; changes to a subclass are not reflected in its superclass.

In OO parliance, an inheritance relationship is known as an "Is-A" relationship. To explain this relationship, let's consider an example where we have a superclass called "Animal" and a subclass called "Cat". From a code perspective, the "Cat" class inherits the components of the "Animal" superclass. Therefore, as a client looking to use instances of these classes, I see no difference between them. In other words, if the "Animal" superclass defines a method called "eat( )", I can call that same method on an instance of class "Cat". Thus, class "Cat" is an "Animal". This relationship leads to some interesting dynamic programming capabilities that we'll get into in my next blog.

Defining Inheritance Relationships in ABAP Objects

At this point, you're probably ready to dispense with all the theory and get into some live code examples. In the code sample below, you'll see that it is very easy to define an inheritance relationship between two classes.

CLASS lcl_parent DEFINITION.
PUBLIC SECTION.
METHODS: a,
b.

PRIVATE SECTION.
DATA: c TYPE i.
ENDCLASS.

CLASS lcl_child DEFINITION
INHERITING FROM lcl_parent.
PUBLIC SECTION.
METHODS: a REDEFINITION,
d.

PRIVATE SECTION.
DATA: e TYPE string.
ENDCLASS.
As you can see in the example above, you can define an inheritance relationship in a class using the INHERITING FROM addition of the CLASS DEFINITION statement. In the example, class "lcl_child" is a subclass of class "lcl_parent". Therefore, "lcl_child" inherits all of the components defined in class "lcl_parent". However, not all of these components are directly accessible in class "lcl_child". Any component defined in the PRIVATE SECTION of "lcl_parent" cannot be accessed in "lcl_child". However, like any other client of class "lcl_parent", "lcl_child" can access these private components through defined "getter" methods, etc. Sometimes, you may want to share access to a component of a parent class with its subclasses without opening up access completely. In this case, you can define components in the PROTECTED SECTION. This visibility section allows you to define components that can be accessed in a given class and any of its subclasses only.

Once the inheritance relationship is defined, you can access a subclass' inherited public components in the same way you would access them in the parent class. Another thing you might notice in the definition of class "lcl_child" is the REDEFINITION addition added to method "a()". The REDEFINITION addition implies that you want to redefine the way that method "a()" is implemented in the "lcl_child" subclass. Keep in mind that these redefinitions only reshape the code in the IMPLEMENTATION part of the class definition. In other words, you cannot change the interface of the method, etc. - otherwise, you would vioate the "is-a" relationship principal. Inside the redefinition of method "a()" in class "lcl_child", you can reuse the implementation of the superclass using the "super" pseudoreference variable like this: super->a( ). You can think of the super pseduoreference as a sort of built in reference variable to an instance of the subclass' superclass.


Some Final Thoughts


Inheritance relationships can be very powerful, allowing you to reuse software components and improve productivity. However, it is important not to get carried away with trying to define complex inheritance hierarchies, etc. Indeed, many top OO researchers advise against the use of inheritance in many design contexts. The bottom line is that there are places where inheritance works, and places it doesn't.

Another important idea to consider is that inheritance is about more than reuse - it's about building relationships. One nice thing about these relationships is that you can define inheritance hierarchies where you have a family of classes that are interchangeable. You can then design your programs generically using plug-and-play techniques - something we'll learn about in my next blog.

OO Programming with ABAP Objects: Encapsulation

In my OO Programming with ABAP Objects: Classes and Objects blog entry, I introduced the concept of classes and objects, showing you how to create and use both in ABAP Objects. If this is your first exposure to OO programming, you might be wondering what's so great about it. After all, on the surface, a class looks a lot like a function group or subroutine pool. In this blog, we will dig deeper to see where classes differentiate themselves from procedural concepts.


What's Wrong with the Procedural Approach?


One common misconception about OO programming is that it is different from procedural programming in every way - not true. There are many important lessons to be taken from the procedural approach. However, there are certain limitations of this approach that influenced early researchers in their design of the OO paradigm. These limitations are best described with an example. Let's imagine that you want to build a code library to make it easier to work with dates. To do so, you create a function group called ZDATE_API that contains various function modules to manipulate and display dates.

From a data perspective, you have a couple of options. Function groups allow you to define group-specific data objects that can be utilized within function modules (similar to the use of attributes in methods). However, in practice, such data objects can be difficult to use. This is because it is not possible to create "instances" of function groups. For example, in the ZDATE_API function group, I might define a global data object of type SCALS_DATE to keep track of the date being manipulated by the function modules. However, if I need to keep track of multiple dates in my program (e.g. created on date, changed on date, document date, etc.), I need to build an internal table to keep track of each date "instance". I also need to keep track of the key to this table externally - otherwise I have no way of identifying a particular date instance. This limitation causes most developers to keep track of their data objects outside of the function group. If you think back to the last time you tried to call a BAPI and all the data objects you had to define beforehand, you'll appreciate what I mean. We'll explore some of the implications of this approach to data in a moment.

Assuming that we elected to keep track of data separately, let's look at what a function module might look like in our ZDATE_API function group. For the purposes of this discussion, we'll keep it simple and just look at a function module used to set the "day" value of the date:

FUNCTION z_date_set_day.
* Local Interface IMPORTING VALUE (lv_day) TYPE I
*                 CHANGING (cs_date) TYPE SCALS_DATE
*                 EXCEPTIONS invalid_date
DATA: lv_month_end TYPE i. "Last Day of Month

CASE cs_date-month.
WHEN 1.
lv_month_end = 31.
WHEN 2.
...
ENDCASE.

IF iv_day LT 1 OR iv_day GT lv_month_end.
RAISE invalid_date.
ELSE.
cs_date-day = iv_day.
ENDIF.
ENDFUNCTION.

This contrived example simply ensures that we initialize the "day" value of a date to a proper value. Clearly, there are probably better ways to implement something like this, but the point is that we have defined some business rules inside of a function module that is part of an API designed to simplify the way that we work with dates.

Now that we have our function group in place, let's imagine that you are asked to troubleshoot a program that is using your function group to display dates in various formats but it is outputting them incorrectly (e.g., 02/31/2009). At first, you might think that there is a problem with Z_DATE_SET_DAY as the day value is incorrect. However, after further review, you discover that the invalid assignment was made in the program itself. After all, there's nothing stopping a program from changing the day value of a local variable directly. To that program, the day component of the SCALS_DATE structure is nothing more than a 2 digit numeric character with a valid range of 00-99 - the semantic meaning of the day value is defined within the confines of our ZDATE_API function group.

Beyond the issue of data corruption, think about how clumsy the typical function group is. The separation of data and behavior in the ZDATE_API function group limits the usefulness of the abstraction, making the use of the API awkward as we have to pass the SCALS_DATE object back and forth between function calls. This becomes something more than a nuisance when the library expands. For example, think of the impact of expanding this API to support timestamps. A better approach would be to hide this data such that callers don't have to worry about it.


Hiding the Implementation


In programming terms, a function group like ZDATE_API is an abstract data type (or ADT). As the term suggests, an ADT abstracts a particular concept into an easy-to-use data type. Ideally, the creation of a date API would imply that we no longer need to worry about how dates work. Rather, we can leverage the hard work (and testing) that went into the creation of the date API and focus in on other important tasks. However, this is difficult to do if the ADT is not wellencapsulated. The term encapsulate implies that we're combining something into an enclosure (or capsule). In the case of an ADT, we're grouping data and behavior together. Moreover, encapsulation also suggests that we are protecting these resources from external tampering. Initially, most programmers balk at this, preferring to have complete control over all parts of the code. The problem with this is that taking control of any code also implies that you assume some of the risk for ensuring that it operates correctly. In our date example, look at how problematic it was to allow external programs to modify the date structure. Ideally, we would prefer that any modifications to this structure pass through business rule checkpoints to make sure that data is not corrupted.

Encapsulation is a good engineering practice used in many disciplines. For instance, you don't have to know how a car works in order to drive it. Of course, it does help if you have power steering, automatic transmission, etc. These features represent the "interface" that users utilize to interact with the car. ADTs also have an interface (namely the signature of the function modules, method, etc.). Good interface design should make it easy to use an API without diminishing any of its capabilities. Another advantage of this engineering approach is that parts become more interchangeable. For example, imagine that a car manufacturer decides to redesign their fuel injector to improve performance. As long as the new fuel injector has the same interface (e.g. same "hookup"), the manufacturer can swap the parts and nobody's the wiser. In my next two blogs, I'll show how inheritance and polymorphism allows you to do some powerful things here with your OO programs.

Hopefully by now you agree that it is a good idea to group data and behavior together in a class. This, by itself, does not mean that a class is encapsulated. Remember, to achieve this, we must also place a protective capsule around the resources. OO languages such as ABAP Objects allow you to define component visibilities using access specifiers. The following class shows how to define these component visibilities:

CLASS lcl_visible DEFINITION.
PUBLIC SECTION.
DATA: x TYPE i.
PROTECTED SECTION.
DATA: y TYPE i.
PRIVATE SECTION.
DATA: z TYPE i.
ENDCLASS.

As you can see, the components of class lcl_visible are partitioned into three distinct sections: the PUBLIC SECTION, the PROTECTED SECTION, and the PRIVATE SECTION. Components defined in the PUBLIC SECTION are visible everywhere. Components defined in the PRIVATE SECTION are only visible within the class itself. Thus, the only place that you can access the attribute "z" would be inside of an instance method of class lcl_visible. We'll talk about the PROTECTED SECTION when we talk about inheritance.

In this way, we can reproduce our date API in a class like this:

CLASS lcl_date DEFINITION.
PUBLIC SECTION.
METHODS: set_month IMPORTING im_month TYPE i
EXCEPTIONS invalid_date,
set_day   IMPORTING im_day TYPE i
EXCEPTIONS invalid_date,
set_year  IMPORTING im_year TYPE i
EXCEPTIONS invalid_date.
PRIVATE SECTION.
DATA: date TYPE scals_date.
ENDCLASS.

CLASS lcl_date IMPLEMENTATION.
METHOD set_month.
"Implementation of method set_month goes here...
ENDMETHOD.

METHOD set_day.
DATA: lv_month_end TYPE i. "Last Day of Month

CASE date-month.
WHEN 1.
lv_month_end = 31.
WHEN 2.
...
ENDCASE.

IF iv_day LT 1 OR iv_day GT lv_month_end.
RAISE invalid_date.
ELSE.
date-day = iv_day.
ENDIF.
ENDMETHOD.

METHOD set_year.
"Implementation of method set_year goes here...
ENDMETHOD.
ENDCLASS.

Notice that the "date" attribute is defined in the PRIVATE SECTION of the class. Now, any accesses to the "date" attribute must go through public instance methods. This ensures that an external program cannot accidentally (or purposefully) modify the value incorrectly. It also makes the date API easier to use as client programs now only need to define dates like this:

DATA: lr_date TYPE REF TO lcl_date.

With a simple API like this, it's not such a big deal. However, consider how you might implement an API for working with SAP Business Partners, etc. Having the objects keep track of all that data simplifies API use considerably.


Reflections


As you have seen in this blog, encapsulation is a good engineering practice that you can use to develop qualityreusable class libraries. I emphasize reusable here to demonstrate an important point. One of the most common reasons why a library is not reused is because it has too many dependencies. Developing classes using implementation hiding techniques forces you to enter into a mindset whereby classes begin to take on a certain amount of autonomy. In other words, you start to ask yourself questions like "What data does an object of my class need in order to do its job?", etc. Once you have figured this out, you design your interface in such a way as to only provide what the class with what it needs - reducing unnecessary dependencies along the way. The fewer dependencies a class has, the less likely things are to go wrong. And, as we will see in my next blog, it also allows us to expand our libraries in interesting ways without jeopardizing code that has been proven to work.

OO Programming with ABAP Objects: Classes and Objects

Before you can begin to grasp OO-related concepts such as inheritance or polymorphism, you must first understand the fundamental concepts of classes and objects. This article will introduce you to these ideas.


Why do we need classes?


With all of the robust data object types available in the ABAP programming language, you might wonder why we even need classes in the first place. After all, isn't a class just a fancy way of defining a structure or function group? This limiting view has caused many developers to think twice about bothering with OO development. As you will see, there are certain similarities between classes and structured data types, function groups, etc. However, the primary difference with classes centers around the quality of abstraction. Classes group data and related behavior(s) together in a convenient package that is intuitive and easy to use. This intuitiveness comes from the fact that classes are modeled based on real-world phenomena. Thus, you can define solutions to a problem in terms of that problem's domain - more on this in a moment.


Classes and Objects: Defined


Over the years, the term class has been adopted by most OO languages to describe the concept of an abstract type. Here, the use of the word "class" suggests that developers are surveying the problem domain and "classifying" objects within that environment. For example, when developing a financial system, it stands to reason that you might identify classes to represent accounts, customers, vendors, etc. Generally speaking, any noun in a functional specification could suggest a particular object. Of course, it is important to remember that a noun describes a person, place, thing, or idea. In the financial example above, it is easy to identify things like accounts, etc. However, an abstract concept like a dunning process is an equally good candidate for a class.

You can think of a class as a type of blueprint for modeling some concept that you are simulating in a problem domain. Inside this blueprint, you specify two things: attributes and behaviors. An attribute describes certain characteristics of the concept modeled by the class. For instance, if you were creating a "Car" class, that class might have attributes such as "make", "model", "color", etc. Technically, attributes are implemented using various data objects (e.g. strings, integers, structures, other objects, etc.). The behavior of the class is defined using methods. The "Car" class described earlier might define methods such as "drive( )", "turn( )", and "stop( )" to pattern actions that can be performed on a car. The figure below shows an example of the class "Car" with some basic attributes and methods.

Car Class Example

The "Car" class described above only defines a blueprint for building a car - and not the car itself. Taking the blueprint metaphor a step further, consider the difference between a set of blueprints for a house and a house that is built in reference to those blueprints. The blueprints for a house describe basic dimensions, layouts, etc. In other words, they provide instructions for building a house. A homebuilder takes these specifications and builds an instance of this house. An instance of a house has a unique physical address and can be customized to suit a persons preferences. In OO-parlance, an instance of a class is called an object. The relationship between a class and object instances of that class is shown in the figure below.

House Blueprint Example


Defining Classes in ABAP Objects


Now that you know what a class is, let's look at how to define one using ABAP syntax. In ABAP, a class is developed in two parts: a definition section and an implementation section. The definition section for the "Car" class described above looks like this:

CLASS lcl_car DEFINITION.
  PUBLIC SECTION.
    METHODS:
      drive IMPORTING im_driving_speed TYPE i,
      turn IMPORTING im_direction TYPE c,
      stop.
  PRIVATE SECTION.
    DATA:
      make TYPE string,
      model TYPE string,
      color TYPE string,
      driving_speed TYPE i.
ENDCLASS.

In my next blog, we'll go into more details about the PUBLIC SECTION and PRIVATE SECTION specifiers you see in the class definition. For now, it is enough to simply note that we have defined a class called lcl_car that contains methods and attributes. As you can see, attributes are defined using the same data types you would use to define a global or local variable in a non-OO context. Similarly, methods can be defined to have various parameter types just like form routines or function modules.

Presently, our lcl_car class does not have any implementation for the methods drive( ), turn( ), and stop( ). These methods must be implemented in the implementation section of a class definition. The syntax for the implementation section is shown below:

CLASS lcl_car IMPLEMENTATION.
  METHOD drive.
    driving_speed = im_driving_speed.
    WRITE: / 'Current speed is:', driving_speed.
  ENDMETHOD.

  METHOD turn.
    IF im_direction EQ 'L'.
      WRITE: / 'Turning left...'.
    ELSE.
      WRITE: / 'Turning right...'.
    ENDIF.
  ENDMETHOD.

  METHOD stop.
    driving_speed = 0.
    WRITE: / 'Stopped.'.
  ENDMETHOD.
ENDCLASS.
Now that our class is fully defined, we will do something useful with it in the next section.


Instantiating and Using Objects


Once a class is defined, you can create instances of that class in your programs. ABAP does a really nice job of abstracting the instantiation process so creating an object is a breeze. However, the abstraction process implies that you do not have direct access to an object at runtime. Rather, you work with objects via an object reference variablethat points to the object. Object reference variables are defined like this:

DATA: lr_car TYPE REF TO lcl_car.

The previous syntax defines an object reference variable called "lr_car" that references objects of type "lcl_car". Once the reference variable is defined, you can create an object using the following syntax:

CREATE OBJECT lr_car.

The CREATE OBJECT statement asks the ABAP runtime environment to build an object of type lcl_car and store a reference to that dynamically generated object inside the reference variable lr_car. You can think of this reference variable kind of like a remote control that can be used to interface with the object it points to. To "press buttons" on this remote control (i.e. access data, call methods, etc.), you use the object component selector (or "->") operator. The example code below shows how to invoke methods on a generated car object:

lr_car->drive( 55 ).
lr_car->turn( 'R' ).
lr_car->stop( ).

As you can see, one nice thing about objects is that they are really easy to use. Therefore, you don't have to be an OO guru to start using some really handy classes in your programs. Indeed, if you search for classes matching the pattern "CL_ABAP*" in transaction SE24, you will find many useful classes that SAP has provided out of the box with the AS ABAP.


Summary


Hopefully by now you have learned how to create simple classes and use them in your programs. In my next blog, I will show you how to use access specifiers to implement encapsulation and data hiding in your classes.