Skip to Content
Technical Articles

Singleton design pattern – A simple yet useful example

Introduction

The example below are based on my need to hold and transfer two fields from LIKP. It is easy to expand to hold other table structures or single fields.

The datastore could be used to replace the usage of MEMORY ID, which are not very readable / maintanable.

A (very) short introduction for those who are not familiar with the singleton design pattern. The singleton design pattern ensures that only one instance of the class is created. The class contains its own constructor, and in this method logic is placed that ensures the creation of only one instance.

And now for the fun part. I have documented the code(!), so it should be readable with just screenshots.

 

Class definition

The class for the data store:

And the O_DATA_STORE attribute:

Constructor

The constructor for the class is the GET_OBJECT method.

Methods

And two simple PUT and GET methods for the LIKP structure:

 

Use of class

The following is the test program that shows the datastore in use.

 

First the main program:

The next level in the call stack:

And the last level of the call stack:

Conclusion

At this point the LIKP-VBELN contains the number 2. This show that our data store object are kept in memory the whole way through the call stack.

Please note that if you leave the call stack (i.e. start another report), then you will loose the reference to the object.

With this datastore class you can now avoid using MEMORY ID to store and move variables trough the call stack.

Further enhancement

After the initial build of the class I have enhanced it further, and changed the GET-methods with a return parameter to evaluate if the field has been stored.

I also added a RESET-method. This method marks the field as unstored.

This has the advantage of being able to check if the field have been initiated. And also to reset the field after it have been retrieved/used.

METHOD put_berot.
  v_berot = i_berot.
  v_berot_initiated = abap_true.
ENDMETHOD.
METHOD reset_berot.
  v_berot = ''.
  v_berot_initiated = abap_false.
ENDMETHOD.

 

METHOD get_berot.
  e_berot = v_berot.
  r_berot_initiated = v_berot_initiated.
ENDMETHOD.

 

4 Comments
You must be Logged on to comment or reply to a post.
  • Thanks Jan for the presentation.

    Basically the same can be done using function group global data.

    Shouldn’t method GET_OBJECT be named GET_INSTANCE? It gives clearer view that we are trying to get instance of the object and not the object itself.

    In that method you do not need to write ZCL_LE_DATA_STORE=>O_DATA_STORE becasue you are accessing attribute that is defined in the calling class so you can just write:

    IF o_data_store IS NOT BOUND.
      CREATE OBJECT O_DATA_STORE.
    ENDIF.
    
    RO_DATA_STORE = O_DATA_STORE.

    You also have a method CLEAR_OBJECT which you do not use in any way (at least in example).

    Shouldn’t it work like that whenever you set item data (in function PUT_LIKP) the global attribute S_LIKP gets cleared at first and only afterwards data is set?

    So you would have: CLEAR_DATA method where S_LIKP is inialized and then you use PUT_LIKP to fill it in with new data.

    I assume you have declared local data just for step-by-step presentation becasue in practise you could have reduced this statement:

    lo_data_store = zcl_le_data_store=>get_object( ).
    
    ls_likp = lo_data_store->get_likp( ).
    
    lo_data_store->put_likp( ).

    to simple:

    DATA(ls_likp) = zcl_le_data_store=>get_object( )->get_likp( ).
    
    zcl_le_data_store=>get_object( )->put_likp( ).

     

    Regards,

    Marek

  • While I was reading this blog, it triggered a “memory” of mine.   Shared memory – though an older technology still works.

    Very nice blog – it got me thinking – and that’s always a good thing.

    Michelle