Tuesday, 25 April 2017

OOP Updates in ABAP 7.4 and ABAP 7.5

SAP introduced the concept of OO programming in ABAP in the year 2000. Since then, it’s been the recommended method of programming. Let’s describe the new features of ABAP that relate to OO programming.

1 Upcasting/Downcasting with CAST


In OO programming, a downcast is a process in which you turn a generic object, like a monster, into a more specific object, like a green monster. An upcast is the reverse. This functionality has been available in ABAP for a long time, but it gets a lot easier in 7.4.
Consider a situation in which you need to get all the components of a specific dictionary structure. Below shows how you would do this prior to ABAP 7.4. First, call a method of CL_ABAP_TYPEDESCR to get metadata about a certain structure. However, to get the list of components of that structure into an internal table, you need an instance of CL_ ABAP_STRUCTDESCR; this is a subclass of CL_ABAP_TYPEDESCR. Thus, you need to perform a downcast to convert the instance of the parent class into an instance of the subclass.

DATA structure_description TYPE REF TO cl_abap_structdescr.

structure_description

?= cl_abap_typedescr=>describe_by_name( 'ZSC_MONSTER_HEADER' ).

DATA structure_components TYPE abap_compdescr_tab.

structure_components = structure_description->components.

In 7.4, you can do this all in one line by using the CAST constructor operator.

DATA(structure_components2) = CAST cl_abap_structdescr(

cl_abap_typedescr=>describe_by_name( 'ZSC_MONSTER_HEADER' ) )-

>components.

Both codes above perform exactly the same function, but in the latter case you no longer need the STRUCTURE_DESCRIPTION helper variable and you also don’t need the line in which you define the STRUCTURE_COMPONENTS type.

2 Finding the Subclass of an Object Instance


In many other programming languages, it’s possible to work out, given an object reference, what precise subclass that instance is. Prior to 7.5, the ABAP team at SAP resisted this, but after unceasing demand from the folks on SAP Community, SAP’s provided the new IS_INSTANCE_OF statement.

Just to rain on SAP’s parade even more—and this is very cruel, considering that they only added this new feature due to popular demand—some purists would say that any subclass should be able to impersonate its parent without any program knowing the difference, and thus a calling program would not need to know the exact subclass. However, the ABAP world is not as pure as everyone would like, and sometimes knowing this information is actually quite useful—as in the following example.

Here is an example in which we try to get an ALV grid reference and try one subclass after another until the assignment succeeds.

DATA: full_screen_adapter TYPE REF TO cl_salv_fullscreen_adapter,

container_adapter TYPE REF TO cl_salv_grid_adapter.

TRY.

"Presume full screen mode (No Container)

"Fullscreen Adapter (Down Casting)

"Target FULL_SCREEN_ADAPTER = CL_SALV_FULLSCREEN_ADAPTER

"CL_SALV_FULLSCREEN is a subclass of CL_SALV_ADAPTER

full_screen_adapter ?= io_salv_adapter.

"Get the Grid

ro_alv_grid = full_screen_adapter->get_grid( ).

CATCH cx_sy_move_cast_error.

"We must be in container mode

"CL_SALV_GRID_ADAPTER is a subclass of CL_SALV_ADAPTER

container_adapter ?= io_salv_adapter.

ro_alv_grid = container_adapter->get_grid( ).

ENDTRY.

In 7.5, life becomes much easier, as shown below. By using the IS_INSTANCE_OF construct, the purpose of the code becomes much clearer to the reader.

IF io_salv_adapter IS INSTANCE OF cl_salv_fullscreen_adapter.

full_screen_adapter ?= io_salv_adapter.

ro_alv_grid = full_screen_adapter->get_grid( ).

ELSEIF io_salv_adapter IS INSTANCE OF cl_salv_grid_adapter.

container_adapter ?= io_salv_adapter.

ro_alv_grid = container_adapter->get_grid( ).

ENDIF.

The same task can be achieved in a slightly different way by using the TYPE OF construct in conjunction with CASE. In the code below, the exact subclass is determined and the respective branch of the CASE statement ensures that the created instance has the correct subclass by using another new  construct: INTO DATA.

CASE TYPE OF io_salv_adapter.

WHEN TYPE cl_salv_fullscreen_adapter

INTO DATA(full_screen_adapter2).

ro_alv_grid = full_screen_adapter2->get_grid( ).

WHEN TYPE cl_salv_grid_adapter

INTO DATA(container_adapter2).

ro_alv_grid = container_adapter2->get_grid( ).

WHEN OTHERS.

RETURN.

ENDCASE.

The way you have to write the code in examples such as the above isn’t particularly clear; the phrase INTO DATA doesn’t read much like an English sentence and thus could be confusing. Nonetheless, you need to know that the option is available.

3 CHANGING and EXPORTING Parameters


In ABAP, a functional method has until now been defined as a method with one returning parameter and zero to many importing parameters, such as the following:

monster_header = monster->get_details( monster_number ).

Many ABAP programmers liked the fact that you could put the result variable at the start rather than having to put that variable in an EXPORTING parameter. However, they wanted to be able to also have CHANGING and EXPORTING parameters as well—that is, to have their cake and eat it too.

In 7.40, SAP has waved its magic wand, and now you can have it both ways. An example is shown below.

* Local Variables

DATA: monster_number TYPE zde_monster_number VALUE '0000000001',

something_spurious TYPE string,

something_unrelated TYPE string.

DATA(monster_header_record) = lcl_monster=>get_details(

EXPORTING id_monster_number = monster_number

IMPORTING ed_something_spurious = something_spurious

CHANGING cd_something_unrelated = something_unrelated ).

There is no doubt many people will be happy with this, but purists who are used to other languages will be horrified. Good OO design leads you toward small methods that do one thing, and the one thing for functional methods is to output one result. If a functional method suddenly starts giving you back all sorts of other exporting parameters and changes something else, then the method is clearly doing more than one thing, and that’s probably bad design. (For example, there are methods designed to return four values of a polynomial equation, and you could use the new design to put the first value in the RETURNING parameter and the last three values in EXPORTING parameters, but that seems a bit silly. You could just return a structure of four values instead.) Nonetheless, because this is a new feature of 7.4 that you might occasionally find useful, it was important to mention it here.

4 Changes to Interfaces


SAP has tried to take some of the pain out of your daily usage of interfaces in OO programming in 7.4. As you know, an interface is a collection of data declaration and method names and signatures. If a class implements any given interface, then it has to redefine all the interface methods. This is all good, but prior to 7.4 the problem was that some standard interfaces had a really big list of methods, only some of which were relevant, so you had to go through the irrelevant methods, redefining them to have blank implementations.

As of 7.4, if you create an interface and think that some of the methods might not be needed by all classes that implement the interface, then you can say so in the interface definition, as shown below.

INTERFACE scary_behavior.

METHODS: scare_small_children,

sells_mortgages DEFAULT FAIL,

hide_under_bed DEFAULT IGNORE,

is_fire_breather

DEFAULT IGNORE

RETURNING rf_yes_it_is TYPE abap_bool.

ENDINTERFACE. "Scary Behavior

This is an interface all—for example—monster classes should implement. Naturally, all monsters should be able to scare children; thus, don’t put any additions after that method definition. This means that each class implementing that interface is forced to redefine the method by the syntax check. On the other hand, most monsters will not sell mortgages (just the worst of the worst monsters), so don’t force all the classes to implement this method. Because you’ve added DEFAULT FAIL, if a program using an instance of a monster that implements this interface tries to make the monster sell mortgages, and the method hasn’t been implemented, then a runtime error occurs (CX_SY_DYN_CALL_ILLEGAL_METHOD).

Similarly, do not force all monster classes to hide under beds; obviously, the ones that are a thousand feet tall have problems in this area. By adding DEFAULT IGNORE to the end of the definition, we can make sure these classes aren’t forcibly implemented. If the program tells such a monster to hide under the bed, then nothing will happen—just as if a call had been made to an implemented method with no lines of code inside it.

In the same way, not all monsters breathe fire. For the ones that do, the IS_FIRE_ BREATHER method can be implemented to return ABAP_TRUE. If the method is not implemented in any given monster class, then the DEFAULT IGNORE addition is used, and the RETURN parameter will bring back an initial value, which in this case is ABAP_FALSE.

No comments:

Post a Comment