Global Data in ABAP OO Programs
I decided to spend a bit of time looking at some of the blogs being published out on SCN as I found myself with a bit of time. Unfortunately I found a lot of examples of poor quality coding, obsolete techniques and non-nonsensical use of OO programming.
As many people are pushing to learn the latest technologies, unfortunately there are many who have not yet grasped many of the fundamental principles of good quality coding. More often than not I have found that having a solid understanding of the syntax and the basic design principles outlined by the good old gang of four has helped me to quickly get up to speed with new advances in technology. There is no point trying to run, before you can walk and eventually it shows in the work you deliver. Testament to this is the WebDynpro developers that embed business logic in the Views of their WebDynpro components or the OO Developers that use classes in the same way they used to use function modules or subroutines.
If you think you are in the above category, then I urge you to go back and learn the basics. There is nothing to be ashamed of. I recently did it myself. Having been persecuted for so many years for trying to use OO techniques that I learnt at university (as it was not fair to other developers who could not code in OO) , I brushed off my old books and gemmed up.
One of the key principles in programming is called the “Separation of Concerns“. So one of the first things that breaks this principle is the use of global data. I’m sure you have come across a piece of code in a massive program in a huge subroutine and had a mental breakdown deciphering what the impact of your change is going to be. Well if you modularise your code, preferably in OO, you have a lot less to worry about.
The time for using global program variables (yes and even in report writing) can and should pretty much come to an end. Below is a little sample report, which I think exemplifies the usage of passing references and minimal use of the global reference.
*& Report ZKP_TEMPLATE_REPORT
*& A very simple MVC template for creating reports.
*& Note the distinct lack of global variables in the main program and
*& even in my classes. It is far better to pass references into methods,
*& then keep track of everything globally.
* CLASS zcl_report_model DEFINITION
* This is a really simplistic model but you could work with your own
* model objects and better incorporate inheritance if required,
* to better encapsulate your business logic
CLASS zcl_report_model DEFINITION.
” Shared type elements that are reused globally in my application
” are placed here in the model
TYPES: ty_carrid TYPE RANGE OF s_carr_id,
ty_currcode TYPE RANGE OF s_currcode,
tty_scarr TYPE STANDARD TABLE OF scarr WITH KEY carrid.
METHODS: get_data IMPORTING im_carrid TYPE zcl_report_model=>ty_carrid
im_currcode TYPE zcl_report_model=>ty_currcode
RETURNING value(re_scarr) TYPE tty_scarr.
” This is the only piece of global data I use in my application
” it exists only as long as I have a reference to an instance of
” this class in my program
DATA: gt_scarr TYPE tty_scarr.
ENDCLASS. “zcl_report_model DEFINITION
* CLASS zcl_report_view DEFINITION
* Your view is really how you intend to present the information. This
* class is abstract and so is the method implying you need to redefine
* it. (Hence no implementation required)
* You may want to render your view in different ways ALV, FILE, EMAIL
CLASS zcl_report_view DEFINITION ABSTRACT.
METHODS: display_data ABSTRACT IMPORTING im_scarr TYPE zcl_report_model=>tty_scarr.
ENDCLASS. “zcl_report_view DEFINITION
* CLASS zcl_report_view_alv DEFINITION
* So here is my new class that will implement the display method
* using an ALV. You can do a lot here with the SALV class for
* displaying data, but this will be very simple.
CLASS zcl_report_view_alv DEFINITION FINAL INHERITING FROM zcl_report_view.
METHODS: display_data REDEFINITION.
* CLASS zcl_report_controller DEFINITION
* This class will be used to handle the flow of the program. Passing
* information from the views and requesting data from the model.
* No business logic should be placed here
CLASS zcl_report_controller DEFINITION ABSTRACT FINAL.
” Import all the report selection variables into the controller method
” for executing the report
IMPORTING im_carrid TYPE zcl_report_model=>ty_carrid
im_currcode TYPE zcl_report_model=>ty_currcode.
ENDCLASS. “zcl_report_controller DEFINITION
” I need a global data statement for my selection option
” I could create a generic select-option by adding the
” name in brackets, however I lose the benefits from the data dictionary
” Search helps etc in my selection screen
” Beyond this I will not use this reference again.
” It’s not ideal but reporting was never going to be 100% OO
DATA: gt_scarr TYPE scarr.
” Replace these with your report variables
SELECT-OPTIONS: s_carrid FOR gt_scarr–carrid,
s_curcod FOR gt_scarr–currcode.
” Now kick off the report logic
zcl_report_controller=>execute_report( im_carrid = s_carrid
im_currcode = s_curcod ).
* CLASS zcl_report_controller IMPLEMENTATION
CLASS zcl_report_controller IMPLEMENTATION.
DATA: lo_model TYPE REF TO zcl_report_model,
lt_scarr TYPE zcl_report_model=>tty_scarr,
lo_disp TYPE REF TO zcl_report_view,
lo_disp_alv TYPE REF TO zcl_report_view_alv.
CREATE OBJECT lo_model.
” Execute the model method to retrieve the data I need for my report
lt_scarr = lo_model->get_data( im_carrid = im_carrid
im_currcode = im_currcode ).
CREATE OBJECT lo_disp_alv.
” Now output the data via the view
” The reason I have assigned the specialised ALV class reference to the
” generalised view class reference is purely overkill in this example.
” However I can very quickly create a new view class and assign this
” perhaps using a case statement to determine which view to output.
lo_disp = lo_disp_alv.
lo_disp->display_data( im_scarr = lt_scarr ).
ENDCLASS. “zcl_report_controller IMPLEMENTATION
* CLASS zcl_report_view IMPLEMENTATION
CLASS zcl_report_view_alv IMPLEMENTATION.
DATA: lo_salv_tab TYPE REF TO cl_salv_table,
lx_salv_msg TYPE REF TO cx_salv_msg,
lt_scarr TYPE zcl_report_model=>tty_scarr.
lt_scarr = im_scarr.
” The SALV classes are excellent for quickly generating output for display
” The factory method here generates a new instance based on my input data.
” I could manipulate the output further prior to display if I need
cl_salv_table=>factory( IMPORTING r_salv_table = lo_salv_tab
CHANGING t_table = lt_scarr ).
CATCH cx_salv_msg INTO lx_salv_msg.
” You should use better exception handling here. Possibly raise it up
” to the controller class and deal with an appropriate action there
MESSAGE e000(zkp) WITH ‘An error occurred rendering the results’(001)
‘as an ALV’(002).
ENDCLASS. “zcl_report_view IMPLEMENTATION
* CLASS zcl_report_model IMPLEMENTATION
CLASS zcl_report_model IMPLEMENTATION.
DATA: lt_scarr TYPE tty_scarr.
IF gt_scarr IS NOT INITIAL.
re_scarr = gt_scarr.
INTO TABLE lt_scarr
WHERE carrid IN im_carrid
AND currcode IN im_currcode.
IF sy–subrc = 0.
re_scarr = gt_scarr = lt_scarr.
ENDCLASS. “zcl_report_model IMPLEMENTATION
At this point I won’t be mentioning singleton classes in the context of global data, but they do have their place. And there is a pretty good blog out there discussing this in ABAP.