ABAP OO - Method interface
What should method interfaces currently look like and how do you achieve this? In this article we will clarify the question.
Table of contents
What should a method interface look like nowadays and what do small interfaces actually bring you in your daily work? We want to take a closer look at this question and also take a closer look at the past in SAP.
Past
In the past, interfaces were defined quite simply and each parameter had its own variable. This also makes sense with RFC function modules, since complex data types are difficult to address from the outside. There are also BAPIs in the system in various forms and complexities. If you have implemented one in your coding recently, you will know what we are talking about, the interfaces are usually not very clear.
Challenge
In object-oriented programming, such "monster interfaces" are not very practical, since they are difficult to pack into an IF statement and the methods do not make particularly small. So just imagine the following scenario:
- Your class needs to be expanded and you want to inject one or more new parameters into the class via the main method in order to work with it internally.
- Your interface uses individual parameters so that you can properly regulate the data transfer.
The class could now look like this and just implement a simple logic for demonstration in which we pass the parameters to other methods in order to use them there.
CLASS zcl_interface_not_clean DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
tt_r_bukrs TYPE RANGE OF t001-bukrs,
tt_r_waers TYPE RANGE OF t001-waers,
tt_t001 TYPE STANDARD TABLE OF t001 WITH EMPTY KEY.
METHODS:
main
IMPORTING
it_r_bukrs TYPE tt_r_bukrs
it_r_waers TYPE tt_r_waers
id_butxt TYPE t001-butxt
id_test TYPE abap_bool.
PROTECTED SECTION.
PRIVATE SECTION.
METHODS:
select_data
IMPORTING
it_r_bukrs TYPE tt_r_bukrs
it_r_waers TYPE tt_r_waers
EXPORTING
et_t001 TYPE tt_t001,
update_data
IMPORTING
it_t001 TYPE tt_t001
id_butxt TYPE t001-butxt
id_test TYPE abap_bool.
ENDCLASS.
CLASS zcl_interface_not_clean IMPLEMENTATION.
METHOD main.
select_data(
EXPORTING
it_r_bukrs = it_r_bukrs
it_r_waers = it_r_waers
IMPORTING
et_t001 = DATA(lt_t001)
).
update_data(
EXPORTING
it_t001 = lt_t001
id_butxt = id_butxt
id_test = id_test
).
ENDMETHOD.
METHOD select_data.
SELECT *
FROM t001
WHERE bukrs IN @it_r_bukrs
AND waers IN @it_r_waers
INTO TABLE @et_t001.
ENDMETHOD.
METHOD update_data.
DATA(lt_t001) = it_t001.
LOOP AT lt_t001 REFERENCE INTO DATA(lr_t001).
lr_t001->butxt = id_butxt.
ENDLOOP.
IF id_test = abap_false.
UPDATE t001 FROM TABLE lt_t001.
ENDIF.
ENDMETHOD.
ENDCLASS.
For example, if we want to implement a new range for querying the data from table T001, we have to adjust some places, which takes more time and is more complex.
Solution
There is also a corresponding entry in the Clean ABAP repository for this. It is also recommended to use an importing and a returning parameter, as this is the easiest way to use the methods. The challenge now lies in setting up its interfaces accordingly. There are basically two tried and tested methods available to you here:
- Use of a structure with the corresponding characteristics on the structure level
- Use of an object as a data container
Both methods also have their charm, but we recommend the object because you are most flexible with it and can attach further logic and validations directly to the data. In addition, an object can be changed, which is not possible with a structure that is only transferred as an import.
For this we create a configuration in the next step, for simplicity we do not use an interface, but build the class directly. As we explained to you in ABAP Unit, an interface should be used for easier testability:
CLASS zcl_test_config DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
tt_r_bukrs TYPE RANGE OF t001-bukrs,
tt_r_waers TYPE RANGE OF t001-waers.
DATA:
mt_r_bukrs TYPE tt_r_bukrs,
mt_r_waers TYPE tt_r_waers,
md_butxt TYPE t001-butxt,
md_test TYPE abap_bool.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_test_config IMPLEMENTATION.
ENDCLASS.
At this point we will now rebuild our output class once and use the new configuration object. During the handover, the interface is already much smaller and can now be easily expanded.
CLASS zcl_interface_clean DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
tt_t001 TYPE STANDARD TABLE OF t001 WITH EMPTY KEY.
METHODS:
main
IMPORTING
io_config TYPE REF TO zcl_test_config.
PROTECTED SECTION.
PRIVATE SECTION.
METHODS:
select_data
IMPORTING
io_config TYPE REF TO zcl_test_config
RETURNING VALUE(rt_t001) TYPE tt_t001,
update_data
IMPORTING
it_t001 TYPE tt_t001
io_config TYPE REF TO zcl_test_config.
ENDCLASS.
CLASS zcl_interface_clean IMPLEMENTATION.
METHOD main.
DATA(lt_t001) = select_data( io_config ).
update_data( it_t001 = lt_t001 io_config = io_config ).
ENDMETHOD.
METHOD select_data.
SELECT *
FROM t001
WHERE bukrs IN @io_config->mt_r_bukrs
AND waers IN @io_config->mt_r_waers
INTO TABLE @rt_t001.
ENDMETHOD.
METHOD update_data.
DATA(lt_t001) = it_t001.
LOOP AT lt_t001 REFERENCE INTO DATA(lr_t001).
lr_t001->butxt = io_config->md_butxt.
ENDLOOP.
IF io_config->md_test = abap_false.
UPDATE t001 FROM TABLE lt_t001.
ENDIF.
ENDMETHOD.
ENDCLASS.
Conclusion
With the appropriate technology and a certain foresight, it should be possible to keep the interfaces very small and to leave your objects open for expansion in the future without having to completely rebuild and expand the interfaces.