ABAP OO - Interface and abstract class
We had already introduced you to inheritance in ABAP OO and are now looking at interfaces and abstract classes and how they help you with modeling.
Table of contents
In one of the last articles on ABAP OO, we went into a little more about inheritance and how it can help you reuse and encapsulate objects. Today we're going to look at the blueprints in object-oriented programming and what you can do with them.
Blueprint
In the world of objects you will always come to places where you want to use flexible objects that reuse certain methods and have certain attributes and yet should be able to behave differently. For such objects you need the same "blueprint" and individual characteristics.
You can also do this with a simple class from which you inherit and whose methods you redefine and develop differently accordingly. The original class can then be used in interfaces, but it can also be instantiated and used directly. However, the use as a construction plan can easily be overlooked, since it is already a finished class.
Interface
An interface is a simple blueprint that represents the public interface of a class. In an interface you can define the following objects in the PUBLIC area:
- Types
- Variables
- Method definitions
Let's define a simple interface that contains all types. In addition, the interface contains a method that should create a new result record and keep it in the buffer of the later class:
INTERFACE zif_bs_demo_interface PUBLIC.
TYPES:
BEGIN OF ts_result,
identifier TYPE sysuuid_x16,
subrc TYPE sy-subrc,
name TYPE string,
END OF ts_result,
tt_result TYPE SORTED TABLE OF ts_result WITH UNIQUE KEY identifier.
DATA:
gt_buffer TYPE tt_result.
METHODS:
create_result
IMPORTING
id_name TYPE string
RETURNING VALUE(rs_result) TYPE ts_result.
ENDINTERFACE.
If a class then implements this interface, we have to define the corresponding method, otherwise we cannot activate the class. The implementation of the class could look like this:
CLASS zcl_bs_demo_interface DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES zif_bs_demo_interface.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_bs_demo_interface IMPLEMENTATION.
METHOD zif_bs_demo_interface~create_result.
rs_result = VALUE #(
identifier = cl_system_uuid=>create_uuid_x16_static( )
subrc = 0
name = id_name
).
INSERT rs_result INTO TABLE zif_bs_demo_interface~gt_buffer.
ENDMETHOD.
ENDCLASS.
As you can see, we only had to program the method and get the type and the global variable for free. This works the same way for large interfaces. You should remember, however, that every new class that uses the interface must implement all methods, even if they may not have any content. How do you best use the interface and pass it on in the individual interfaces? We have created a small class for you to use:
CLASS zcl_bs_demo_interface_usage DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
METHODS:
output_buffer
IMPORTING
io_demo TYPE REF TO zif_bs_demo_interface
io_out TYPE REF TO if_oo_adt_classrun_out.
ENDCLASS.
CLASS zcl_bs_demo_interface_usage IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA:
lo_interface TYPE REF TO zif_bs_demo_interface,
lo_class TYPE REF TO zcl_bs_demo_interface.
lo_interface = NEW zcl_bs_demo_interface( ).
lo_interface->create_result( `John Doe` ).
output_buffer( io_demo = lo_interface io_out = out ).
lo_class = NEW #( ).
lo_class->zif_bs_demo_interface~create_result( `Jane Doe` ).
output_buffer( io_demo = lo_class io_out = out ).
ENDMETHOD.
METHOD output_buffer.
io_out->write( io_demo->gt_buffer ).
ENDMETHOD.
ENDCLASS.
In the example we define a method to output the class buffer and thus simulate the use of the reference by passing it to the method. We use the interface directly for typing, which has two advantages when it comes to use. First, we can pass different classes based on our interface. Second, we only have access to the common attributes and methods, which ensures uniform handling.
When creating the instance in the MAIN method, the transfer works on the basis of the interface and the concrete implementation of the class, the basis here is the implementation of the common interface. However, you will notice that there are differences when creating the instance and when calling the method. Both variants have advantages and disadvantages when using them. It is recommended that you work uniformly via an instance based on the interface.
Abstract class
So what makes the abstract class so special and how can it serve as a blueprint for us? The abstract class is a real class and can therefore also have method implementations. However, method definitions can also be created that initially have no implementation. Such classes have the keyword ABSTRACT and can therefore no longer be instantiated directly. Methods that serve as envelopes and are to be implemented later are also identified with the keyword ABSTRACT.
As an example, we have reused the definition and the logic of the interface example. The implementation is located directly in the abstract class. We also define an abstract method that we then want to implement in the inheriting class.
CLASS zcl_bs_demo_abstract DEFINITION PUBLIC ABSTRACT CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ts_result,
identifier TYPE sysuuid_x16,
subrc TYPE sy-subrc,
name TYPE string,
END OF ts_result,
tt_result TYPE SORTED TABLE OF ts_result WITH UNIQUE KEY identifier.
DATA:
gt_buffer TYPE tt_result.
METHODS:
create_result
IMPORTING
id_name TYPE string
RETURNING VALUE(rs_result) TYPE ts_result,
create_empty_entry ABSTRACT.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_bs_demo_abstract IMPLEMENTATION.
METHOD create_result.
rs_result = VALUE #(
identifier = cl_system_uuid=>create_uuid_x16_static( )
subrc = 0
name = id_name
).
INSERT rs_result INTO TABLE gt_buffer.
ENDMETHOD.
ENDCLASS.
So that we can also use and instantiate the abstract class, we have to create an implementing class.
CLASS zcl_bs_demo_abstract_impl DEFINITION PUBLIC FINAL CREATE PUBLIC
INHERITING FROM zcl_bs_demo_abstract.
PUBLIC SECTION.
METHODS:
create_empty_entry REDEFINITION.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_bs_demo_abstract_impl IMPLEMENTATION.
METHOD create_empty_entry.
ENDMETHOD.
ENDCLASS.
As you can see, the implementation is kept very lean and only the abstract method needs to be redefined and implemented. At this point we wanted to show you again that not the complete logic has to be contained in the abstract class, but that individual methods can also be defined in the inheriting class.
When using the abstract class, it looks so much the same as with the interface, the instantiation can use both types of objects. However, it is also recommended to use the most abstract part here so that the interface works in any case and other implementations of the abstract class can also be used.
CLASS zcl_bs_demo_abstract_usage DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
METHODS:
output_buffer
IMPORTING
io_demo TYPE REF TO zcl_bs_demo_abstract
io_out TYPE REF TO if_oo_adt_classrun_out.
ENDCLASS.
CLASS zcl_bs_demo_abstract_usage IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA:
lo_abstract TYPE REF TO zcl_bs_demo_abstract,
lo_class TYPE REF TO zcl_bs_demo_abstract_impl.
lo_abstract = NEW zcl_bs_demo_abstract_impl( ).
lo_abstract->create_result( `John Doe` ).
output_buffer( io_demo = lo_abstract io_out = out ).
lo_class = NEW #( ).
lo_class->create_result( `Jane Doe` ).
output_buffer( io_demo = lo_class io_out = out ).
ENDMETHOD.
METHOD output_buffer.
io_out->write( io_demo->gt_buffer ).
ENDMETHOD.
ENDCLASS.
Usage
So when should you use which blueprint? Interfaces and abstract classes both have their strengths and weaknesses, so in this section we want to go back to the differences and give tips. An instantiation of interfaces or abstract classes is not possible directly and always requires an inheriting class.
Interface
In theory, you should always start with an interface first, as it is easy to understand and works very well with the Test Double Framework if you use ABAP Unit. In the interfaces, it is clear from the start that a specific class is required to supply the parameter. The interface is also very easy to define and does not require an implementation part.
Abstract class
As soon as you have coding that you want to reuse, the best thing to do is to drive with an abstract class, as you can specify implementations for the inheriting class. But this also gives you the flexibility to overwrite such specifications by redefinition. The Test Double Framework does not work with abstract classes, here you have to use other methods to make your coding testable.
Combined interfaces
You can also combine different interfaces into a larger interface, as you can find in the official ABAP documentation from SAP. The combination and use of several interfaces works, but is not quite as handy. From our personal experience, we only recommend this method to a limited extent.
Conclusion
In this article you learned more about the use of interfaces and abstract classes and when you should use them. Always try an interface first, and if you want to reuse coding, switch to the abstract class concept.