Skip to Content
Technical Articles
Author's profile photo Jörg Krause

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.

Assigned Tags

      8 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Oleg Bashkatov
      Oleg Bashkatov

      Nice blog!)

      However I think that ALV (or any frontend view) is not about Big Data Models 🙂

      How you would deal with exceptions in that case?

      Author's profile photo Jörg Krause
      Jörg Krause
      Blog Post Author

      My actual project has 15 ALV views...

      Author's profile photo Oleg Bashkatov
      Oleg Bashkatov

      It is good, interesting and impressive, but number of ALV-views is not feature of Big Data models.

      I think under "Big Data models" you mean not big data itself, but complex/nested structure. In that case the approach is good.

      Author's profile photo Thomas Jung
      Thomas Jung

      Nothing wrong with your approach but I think everyone should also consider that there is ABAP table sharing (works for strings as well).  Therefore not all by value operations result in a copy.  ABAP defers the copy until the first write operation on the internal table. For read operations you might not be duplicating the internal table even when using it in a returning parameter.

      Sharing Between Dynamic Data Objects - ABAP Keyword Documentation (sap.com)

      Author's profile photo Jörg Krause
      Jörg Krause
      Blog Post Author

      Yes I know this feature. But what about a structure containing several tables as well as some structures as components? Are those embedded tables shared as well?

      Author's profile photo Oleg Bashkatov
      Oleg Bashkatov

      but also on that link they say:

      This means programming must never be based on when table sharing occurs and when it is canceled again.

      So it seems using clear defined pass by reference (object by pointer) is much better...

      Author's profile photo Thomas Jung
      Thomas Jung

      As I said nothing wrong with this approach. But I also think that people reading this might incorrectly take away that pass by value is always a copy operation. Clearly that isn't the case thanks to the shared object concept detailed in the help link.  While it's good to be aware that in some edge cases controlling the reference yourself is a plus, in most scenarios is actually quite fine to let the table sharing do it's work behind the scenes.  I've seen this misunderstand lead people to conclude that all by value (and by extension returning parameters) must be avoided.  This causes them to avoid the more functional programming style in order to avoid a performance/memory issue that most likely wouldn't have occurred anyway.

      Personally I would also make the case that for the approach in this blog post that data_model shouldn't be a nested table or deep structure but should instead just be a class. You would pass the class instance by reference automatically and you wouldn't have to dereference the attributes for access. If you are treating the structure like a class anyway (author's own words) then why not just go ahead and use a class?

      Author's profile photo Oleg Bashkatov
      Oleg Bashkatov

      Thanks for the clarification with returning. Actually some years ago I received exactly the same comment about returning parameters with the same reason. But anyway I was continued to use it because it improved readability 🙂  thanks for the link.

      as for the class - it is good, agree. But for me it would be confusing to call class-object data_model. Because model as a rule - it is plenty of fields. Class - is type of object/instance. I would call it billing_model_manager or sales_info_provider but not data_model. (but it is just my strange opinion 🙂 )