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.
DATA: c TYPE i.
CLASS lcl_child DEFINITION
INHERITING FROM lcl_parent.
METHODS: a REDEFINITION,
DATA: e TYPE string.
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.