8.3    Inheritance

Inheritance is an important concept in OOP that helps us avoid repetition of code and leverage existing code libraries. In Section 8.1, we described how inheritance makes it easy to expand functionality without having to rewrite existing code. With procedural programming techniques, the closest you can get to something similar to inheritance is either editing the existing code to expand the functionality or copying the existing code and then adding the additional functionality. Both approaches are undesirable, because editing existing code may break the original functionality, and it requires a lot of testing to check that the original and the new functionality both work correctly, which can itself be a time-consuming process to go through each time the functionality is enhanced. If we copy the existing code, not only is the code now redundant, but also it leads to a maintenance nightmare, because each update to the original code needs to be replicated in all of the copies.

Inheritance allows you to expand a class to meet more specific requirements while reusing what’s already been developed without having to repeat the existing code. For example, say that during the initial stage of development you defined a class to process information about students. Because your requirement didn’t differentiate between types of students, you developed a generic student class.

However, later in the development process, the requirement has been enhanced to branch students into various categories—commerce students, IT students, science students, and so on—with each student category required to perform specific tasks. As the requirement becomes more specific, you can simply inherit the original student class to add more specific functionality for each student category, as shown in Listing 8.17. This allows you to move from generic functionality to specific functionality without breaking the existing implementation while reusing the effort that already went into the original development.

CLASS cl_student DEFINITION.
PUBLIC SECTION.
METHODS tuition_fee.
ENDCLASS.
CLASS cl_commerce_student DEFINITION INHERITING FROM cl_student.
PUBLIC SECTION.
METHODS tuition_fee REDEFINITION.
METHODS subject. "New method for additional functionality
ENDCLASS.
CLASS cl_commerce_student IMPLEMENTATION.
METHOD tuition_fee.
"Logic to calculate tuition fee for commerce students goes here
ENDMETHOD.
METHOD subject.
ENDMETHOD.
ENDCLASS.

Listing 8.17    Inheritance

In Listing 8.17, the new cl_commerce_student class is a subclass of the original student superclass from which it’s inheriting, cl_student.

In this section, we’ll look at the different components of the inheritance concept. We’ll then look at both abstract and final classes and methods before moving on to composition relationships. In the final subsection, we’ll look at how to use the refactoring assistant in Class Builder to move class components in the inheritance tree.

8.3.1    Inheriting Components

In Listing 8.16, we were only concerned with what can be accessed from within and outside of a class. The public section allowed access from external applications, while the private section restricted access to components within the class.

Inheritance brings a new dimension to implementation hiding. With the protected section, you can restrict access to the components from external applications but allow access to the subclasses. Therefore, for external programs, the components defined in the protected section are similar to the components in the private section. This allows you to provide access to child classes without exposing the components of a parent class to the public.

Listing 8.18 provides some sample code to demonstrate inheritance. In this example, we’re demonstrating how all the components of the parent class are inherited by the child class.

CLASS cl_parent DEFINITION.
PUBLIC SECTION.
METHODS set_value IMPORTING value TYPE string.
PROTECTED SECTION.
DATA value TYPE string.
METHODS check_value.
PRIVATE SECTION.
METHODS reset_value.
ENDCLASS.
CLASS cl_parent IMPLEMENTATION.
METHOD set_value.
ENDMETHOD.
METHOD check_value.
ENDMETHOD.
METHOD reset_value.
ENDMETHOD.
ENDCLASS.
CLASS cl_child DEFINITION INHERITING FROM cl_parent.
ENDCLASS.
CLASS cl_child IMPLEMENTATION.
ENDCLASS.
DATA child TYPE REF TO cl_child.
START-OF-SELECTION.
CREATE OBJECT child.
Child->set_value( value = 'child' ).

Listing 8.18    Inheritance Example

In Listing 8.18, the cl_child class is defined as a subclass of cl_parent. Here, cl_parent is the superclass, and it implements the set_value method in the public section, the check_value method and the value attribute in the protected section, and the reset_value method in the private section.

The cl_child class is inherited from cl_parent using the inheriting from addition of the class definition statement. This sets the parent-child relationship between the two classes. The cl_child class doesn’t list any components on its own. All the components in the public and protected sections of the cl_parent superclass are, by default, available to the cl_child subclass. This is shown in the program code under start-of-selection (see Listing 8.18), where a reference object child is defined by referring to cl_child and the set_value method of cl_parent is accessed using this child class reference with the statement Child->set_value( value = 'child' ).

Keep the following important points about inheritance in mind:

CLASS cl_parent DEFINITION.
PUBLIC SECTION.
METHODS constructor.
PROTECTED SECTION.
METHODS meth.
ENDCLASS.
CLASS cl_parent IMPLEMENTATION.
METHOD constructor.
Me->meth( ).
ENDMETHOD.
METHOD meth.
WRITE 'I'm in parent class'.
ENDMETHOD.
ENDCLASS.
CLASS cl_child DEFINITION INHERITING FROM cl_parent.
PUBLIC SECTION.
METHODS constructor.
PROTECTED SECTION.
METHODS meth REDEFINITION.
ENDCLASS.
CLASS cl_child IMPLEMENTATION.
METHOD constructor.
super->constructor( ).
Me->meth( ).
ENDMETHOD.
METHOD meth.
WRITE 'I'm in child class'.
ENDMETHOD.
ENDCLASS.
DATA child TYPE REF TO cl_child.
START-OF-SELECTION.
CREATE OBJECT child.

Listing 8.19    Constructor Access in Superclass and Subclass

In Listing 8.19, cl_parent is defined with an instance constructor and the meth method. cl_child inherits the cl_parent class, and the meth method is redefined in the cl_child subclass. A reference object, child, is defined in the program referring to cl_child. When the reference object child is instantiated under start-of-selection using the CREATE OBJECT statement, it will call the constructor of the cl_child class. However, because it’s mandatory to call the parent constructor before any instance components can be accessed, the constructor in cl_child calls the parent constructor using the super->constructor( ) statement.

The meth method is called in the respective constructors of the superclass and subclass. The constructor of the cl_child subclass calls the redefined meth method in cl_child, whereas the constructor of the cl_parent superclass calls the meth method defined in cl_parent. You can keep a breakpoint on the CREATE OBJECT statement and execute the code to see the sequence of the code execution in debug mode for easy understanding.

For global classes in Class Builder, the inheritance relation can be maintained either while creating the class, as shown in Figure 8.3, or under the Properties tab, as shown in Figure 8.4.

Maintaining Superclass Details in Class Builder

Figure 8.3    Maintaining Superclass Details in Class Builder

Maintaining Inheritance under Class Properties

Figure 8.4    Maintaining Inheritance under Class Properties

To redefine a method in the subclass, you can use the Redefine button in Class Builder, as shown in Figure 8.5.

Redefine Button in Class Builder

Figure 8.5    Redefine Button in Class Builder

8.3.2    Abstract Classes and Methods

Sometimes, you may want to define a generic class that can be used as a template that can be implemented in subclasses. Defining a dummy superclass with dummy methods may not be a good way of defining this template. For example, if you have students and various course categories and each student course category has a different process to determine its tuition, then you can define a student class with a tuition_fee method that can be used as a template, and its implementation can be maintained more specifically in the specific subclasses that inherit this class. For example, a commerce_student class can inherit the student class and implement the tuition_fee method specific to commerce students.

If you define this student class as a regular class, then you also need to maintain its implementation. However, because this is a generic class, maintaining the implementation doesn’t make sense. In such scenarios, we can define this student class as an abstract class and define the tuition_fee method as an abstract method.

An abstract class only maintains a definition; no implementation is required for such a class if it contains all abstract components, which are inherited in a subclass in which the specific implementation can be maintained. Listing 8.20 provides an example of using an abstract class.

CLASS cl_student DEFINITION ABSTRACT.
PUBLIC SECTION.
METHODS tuition_fee ABSTRACT.
ENDCLASS.

CLASS cl_commerce_student DEFINITION INHERITING FROM cl_student.
PUBLIC SECTION.
METHODS tuition_fee REDEFINITION.
ENDCLASS.
CLASS cl_commerce_student IMPLEMENTATION.
METHOD tuition_fee.
"Logic to calculate tuition fee for commerce students goes here
ENDMETHOD.
ENDCLASS.

CLASS cl_science_student DEFINITION INHERITING FROM cl_student.
PUBLIC SECTION.
METHODS tuition_fee REDEFINITION.
ENDCLASS.
CLASS cl_science_student IMPLEMENTATION.
METHOD tuition_fee.
"Logic to calculate tuition fee for science students goes here
ENDMETHOD.
ENDCLASS.

Listing 8.20    Using Abstract Class

In Listing 8.20, cl_student is defined as an abstract class by using the ABSTRACT addition with the CLASS DEFINITION statement.

An abstract method is defined in class cl_student using the ABSTRACT addition with the METHODS statement. Because this class contains an abstract method, it can only be implemented in a subclass by using the redefinition addition. If the class contains a regular method (not an abstract method), then we need to maintain the implementation of that method in the implementation part of the class cl_student. This implementation then would be part of the template that the subclasses inherit. Because there are no regular methods in Listing 8.20, we’ve ignored the implementation of the cl_student class completely.

The code in Listing 8.20 defines two subclasses—cl_commerce_student and cl_science_student—which are inherited from the cl_student abstract class. The tuition_fee method is redefined in the respective subclasses to implement specific functionality.

Note

It isn’t possible to create an instance of an abstract class, because an abstract class is only used as a template.

For global classes, the abstract property for a class is set under the Properties tab in the Inst.Generation field, as shown in Figure 8.6.

Abstract Class in Class Builder

Figure 8.6    Abstract Class in Class Builder

For methods in Class Builder, the abstract property is set by keeping the cursor on the method, selecting the Detail View button, and then selecting the Abstract checkbox, as shown in Figure 8.7.

Defining Abstract Method in Class Builder

Figure 8.7    Defining Abstract Method in Class Builder

8.3.3    Final Classes and Methods

Sometimes, in the inheritance tree, it may not make sense to expand a class further. In such a scenario, you can make the class final, which means it can’t be inherited further. A local class can be defined as a final class by adding FINAL to the CLASS DEFINITION statement, as shown in Listing 8.21.

CLASS cl_final DEFINITION FINAL.
……
ENDCLASS.

Listing 8.21    Defining Final Class

For global classes, to define a class as final in Class Builder, the Final checkbox needs to be selected in the Properties tab, as shown in Figure 8.8.

Methods can also be defined as final so that they can’t be redefined further in the subclass. For example, it may make sense to further inherit a class, but a method in the class may not need to be extended further because it’s purpose is complete. You can make a method final by adding FINAL to the METHODS statement, as shown in Listing 8.22.

CLASS cl_parent DEFINITION.
PUBLIC SECTION.
METHODS student FINAL.
ENDCLASS.

Listing 8.22    Final Method

Final Class

Figure 8.8    Final Class

To make the method of a global class final, keep the cursor on the method in the Class Builder and select the Detail View button. In the Change Method window, select the Final checkbox, as shown in Figure 8.9.

Final Method in Class Builder

Figure 8.9    Final Method in Class Builder

8.3.4    Composition

Using inheritance, we design classes that fit an is a relationship. For example, a commerce student is a type of student, so we define the class commerce_student as a subclass of student in the example in Listing 8.20. Sometimes, in an effort to reuse the existing code, developers create an inheritance tree that doesn’t fit an is a relationship. For example, if we have an existing orders class, it makes sense to create a sales_order class inheriting from the orders class, because a sales order is an order.

However, if you want to define a class for delivery, it may not make sense to inherit the orders class, because a delivery is not an order. However, each order has one or more deliveries associated with it. If the objects fit the has a relationship, we call it a composition relationship.

Composition allows you to reuse existing functionality by maintaining the existing object as an attribute in the class. Therefore, the orders class can have delivery as an attribute in the class. This arrangement allows you to take advantage of the existing functionality in the system, as shown in Listing 8.23.

CLASS cl_delivery DEFINITION.
PUBLIC SECTION.
METHODS get_delivery.
ENDCLASS.
CLASS cl_delivery IMPLEMENTATION.
METHOD get_delivery.
ENDMETHOD.
ENDCLASS.
CLASS cl_orders DEFINITION.
PUBLIC SECTION.
METHODS track_order.
PRIVATE SECTION.
DATA delivery TYPE REF TO cl_delivery.
ENDCLASS.
CLASS cl_orders IMPLEMENTATION.
METHOD track_order.
CREATE OBJECT delivery.
delivery->get_delivery( ).
ENDMETHOD.
ENDCLASS.

Listing 8.23    Composition Example

In Listing 8.23, the cl_delivery class is maintained as an attribute in the cl_orders class. The delivery object is instantiated in the track method to access the delivery information about the order.

Tip

As a rule of thumb, use an inheritance relationship between objects if they fit the is a relationship and use composition if the objects fit the has a relationship. This tip should help you make better design decisions.

8.3.5    Refactoring Assistant

When you’re designing an application, sometimes you may miss defining the class components at the right level in the inheritance hierarchy. For example, you may have defined a method in the subclass that may make more sense in the superclass due to a change in the requirement.

In such a scenario, you can use the refactoring assistant tool in Class Builder to move the class components up or down the inheritance tree. This saves a lot of effort and any chance of missing steps when manually moving the components.

To use the refactoring assistant, select the menu path UtilitiesRefactoringRefactoring in Class Builder, as shown in Figure 8.10.

Refactoring Assistant Menu Path

Figure 8.10    Refactoring Assistant Menu Path

In the Refactoring Assistant window (see Figure 8.11), you can drag and drop components to the right hierarchy level.

Refactoring Assistant Window

Figure 8.11    Refactoring Assistant Window

Now that we’ve walked through the inheritance principles, let’s move on to using polymorphism in OOP.