Pass the pointer: deal with big data models using references in ABAP
Preface: how to pass the parameter?
ABAP offers IMPORTING, EXPORTING, CHANGING and RETURNING. For the first three, there is also BY VALUE and BY REFERENCE as an option. So which one to use?
Personally, I like the short form
result = do_something_on( input ).
So I pass mostly by value. If there are more results to obtain, I normally create a structure for the result:
types: begin of more_results, first_result type string, second_result type string, end of more_results. (...) methods do_something ... returning result type more_results. (...) result = do_something( ... )
I do not dislike multiple input parameters when the number is small, let’s say up to three parameters. When it comes to more inputs, I consider creating a structure as well.
Big data models
This post is about big structures to pass between several classes.
I keep my classes small and let them have their needed data in private attributes. Now, when it comes to complex applications with big data models, this can lead to multiplying the data for each class.
Let’s do an example
class cl_application implementation. method main. id = get_id_from_somewhere( ). data_model = new cl_read( )->read( id ). new cl_front_end( )->show( data_model = data_model controller = me ). endmethod. method get_id_from_somewhere. " get the id from user input or from elsewhere endmethod. endclass.
This small application reads data into a data model that contains a lot of data. For our example, we keep it rather small:
types: begin of header_data, id type string, text type string, date type d, org_level type string, end of header_data. types: begin of item, id type string, item_id type string, text type string, quantity type f, end of item, items type standard table of item with empty key. types: begin of data_model, header_data type header_data, items type items, end of data_model.
I’m sure you can imagine how this model could grow when more details are needed in the application.
The main application stores the data model in an own attribute. Then it calls the front end passing the attribute by reference.
What happens now in the front end, is:
class cl_front_end implementation. method show. me->data_model = data_model. me->controller = controller. start_view( ). endmethod. method start_view. " present the data on screen endmethod. endclass.
The data model is copied to an attribute in the front end, so it can be accessed easily from screen control modules (ALV, trees or others).
Now we have at least two copies of the data model. Adding more functionality leads to more classes that need the data model as well.
Despite the waste of memory, the programmer has also to care about the synchronization of all those data objects.
Use a pointer
If we push around only the pointer instead of the whole data object, we get our classes to work all with the same data. No waste of memory, no syncing problems.
The main program changes then to:
method main. id = get_id_from_somewhere( ). data_model = new cl_read( )->read( id ). new cl_front_end( )->show( data_model = ref #( data_model ) controller = me ). endmethod.
And the new front end would be:
class cl_front_end definition. public section. methods show importing data_model type ref to data_model controller type ref to cl_application. private section. data data_model type ref to data_model. data controller type ref to cl_application. methods start_view. endclass. class cl_front_end implementation. method show. me->data_model = data_model. me->controller = controller. start_view( ). endmethod. method start_view. cl_demo_output=>display( data_model->*-header_data ). endmethod. endclass.
As you see, access to all components of the data model is possible using the dereferencing operator ->*. In fact, we now treat the data model type as a dump class with only data in it.