In this post, I would show you a more flexible design using ABAP Objects. This design is to decouple logic, criteria, data display as much as possible.
Introduction
As discussed in the previous post ABAP Object Oriented Approach for Reports β Initial Design (which got very good response – 14 comments and counting), I see lot of developers following bad design while trying to use Object Oriented. This I generally refer as Pseudo Object Oriented as this design doesn’t leverage any OO features.
I would show you how to implement the example used in previous post with OO approach. This approach is based purely on Model View Controller aka MVC. If you are not yet familiar with MVC, I would suggest you to read my old 3 post series starting with: ABAP Objects Design Patterns β Model View Controller (MVC) Part 1
Participants
For the simple report, I would create three classes:
- SEL – Class for SELECTION criteria
- DATA (Model) – DATA Selection & processing class
- ALV (View) – Class to Display data
Whole idea here is to decouple as much as possible. So, based on that, we would move all the data selection and processing related logic into a DATA class. This DATA Class would have different methods to retrieve and process the data based on the selection criteria. Instead of creating the selection criteria as parameters to the method or Attributes of the data class and having them embedded in the DATA class, I would have them in a separate class – SEL Class. This would provide me an advantage of passing the SEL object to DATA class as well as to ALV class if required. Next step is to decouple the View related functionality out of the report. So, I would create ALV class.
UML for the design
The basic design on UML…
Additionally I have created a Exception class ZCX_MSG to handle the messages raised from the object model. If you are not yet familiar with Class Based Exception, I would suggest you to read one of my multi-post series starting with Class-based Exceptions I β Basics and get used to it.
SEL Class
SEL Class would be responsible to carrying the attributes to Model (DATA) and View (ALV) as needed. Since the selection criteria are not embedded in MODEL class, you could easily pass the object to another model class. SEL Class responsibilities and attributes:
- Contains the attributes for all the parameters and select options from selection screen
- Contains the methods for Default value population on the screen
- Contains the methods to validate the selection screen values
- All the attributes would be only set just when processing the event START-OF-SELECTION
Code lines for SEL Class – ZCL_REPORT_T100_SEL
This demo SEL class has two attributes to hold selection criteria. It also has two methods, GET_DEFAULT for default values and VALIDATE_MESSAGE_CLASS for validation.
Code lines for SEL ZCL_REPORT_T100_SEL class
* Types for the Attributes types: tt_MSGNR_Range type range of t100-MSGNR . data V_ARBGB type T100-ARBGB . data T_MSGNR_RANGE type TT_MSGNR_RANGE . * class ZCL_REPORT_T100_SEL implementation. method GET_DEFAULT. " returning value(RV_ARBGB) type T100-ARBGB . rv_arbgb = '00'. endmethod. * METHOD validate_message_class. " importing !IV_ARBGB type T100-ARBGB " raising ZCX_MSG . DATA: ls_msg TYPE symsg. SELECT SINGLE arbgb INTO v_arbgb FROM t100 WHERE arbgb = iv_arbgb. IF sy-subrc NE 0. ls_msg-msgty ='E'. ls_msg-msgid ='00'. ls_msg-msgno = '398'. ls_msg-msgv1 = 'No Message class found'. RAISE EXCEPTION TYPE zcx_msg EXPORTING msg = ls_msg. ENDIF. ENDMETHOD. endclass.
DATA Class
DATA class would be responsible to fetch the data and prepare it in desired output. Typical DATA Class would preform these:
- Would contain all the methods for data selection
- Would use the attributes from the SEL objects for the data selection and filtration
- Would contain instance attributes. Only the one which are needed to be exposed would be made Public, all the rest would be Private.
- This could in turn use different objects as needed
Demo DATA class – ZCL_REPORT_T100_DATA
This class contains two attributes: O_SEL reference to ZCL_REPORT_T100_SEL and T_T100 to store the selected data. In the method CONSTRUCTOR, we set the O_SEL from importing parameter. Method GET_DATA selects the data using the SEL object attributes.
Code lines for the methods of DATA class ZCL_REPORT_T100_DATA
class ZCL_REPORT_T100_DATA implementation. method CONSTRUCTOR. " importing !IO_SEL type ref to ZCL_REPORT_T100_SEL . me->o_Sel = io_sel. endmethod. * method GET_DATA. SELECT * FROM t100 INTO TABLE t_t100 WHERE arbgb = me->o_sel->v_arbgb AND msgnr IN me->o_Sel->T_MSGNR_RANGE. endmethod. endclass.
ALV class
Typical View class is responsible to display data to the Caller as well as handling any action. ALV class:
- Would contain the logic related to ALV processing
- Also contains the event handler methods to handle any needed events
- Would contain the DATA class an attribute. This would provide the access to the Public attributes thus gaining access to Data contents
Demo ALV class – ZCL_REPORT_T100_ALV
This class contains two attributes O_DATA reference to data class ZCL_REPORT_T100_DATA and O_SALV reference to CL_SALV_TABLE. Contains couple of methods – CONSTRUCTOR to set the DATA object and GENERATE_ALV to generate the simple ALV.
Code lines for the methods of ALV class ZCL_REPORT_T100_ALV
class ZCL_REPORT_T100_ALV implementation. method CONSTRUCTOR. " importing !IO_DATA type ref to ZCL_REPORT_T100_DATA . me->o_Data = io_data. endmethod. * method GENERATE_ALV. TRY. CALL METHOD cl_salv_table=>factory IMPORTING r_salv_table = me->o_salv CHANGING t_table = me->o_data->t_t100. * me->o_salv->display( ). * CATCH cx_salv_msg . ENDTRY. endmethod. endclass.
Program based on Object Oriented Approach
Here is the redesigned version of the procedural code to use the all the classes which we have declared.
*&---------------------------------------------------------------------* *& Purpose - Object Oriented Implementation for a Report *& Author - Naimesh Patel *& URL - http://zevolving.com/?p=2045 *&---------------------------------------------------------------------* REPORT ztest_np_t100_OO_report. * DATA: v_msgnr TYPE t100-msgnr. " for selection screen * DATA: o_data TYPE REF TO zcl_report_t100_data. DATA: o_sel TYPE REF TO zcl_report_t100_sel. DATA: o_alv TYPE REF TO zcl_report_t100_alv. DATA: o_exc TYPE REF TO zcx_msg. * SELECTION-SCREEN: BEGIN OF BLOCK blk1 WITH FRAME TITLE text-t01. PARAMETERS: p_arbgb TYPE t100-arbgb OBLIGATORY. SELECT-OPTIONS: s_msgno FOR v_msgnr. SELECTION-SCREEN: END OF BLOCK blk1. * INITIALIZATION. CREATE OBJECT o_sel. CREATE OBJECT o_data EXPORTING io_sel = o_sel. CREATE OBJECT o_alv EXPORTING io_data = o_data. p_arbgb = o_sel->get_default( ). * AT SELECTION-SCREEN ON p_arbgb. TRY. o_sel->validate_message_class( p_arbgb ). CATCH zcx_msg INTO o_exc. MESSAGE ID o_exc->msg-msgid TYPE o_exc->msg-msgty NUMBER o_exc->msg-msgno WITH o_exc->msg-msgv1 o_exc->msg-msgv2 o_exc->msg-msgv3 o_exc->msg-msgv4. ENDTRY. * START-OF-SELECTION. " Set the attributes of the SEL object from the " Selection screen parameters & Options o_sel->v_arbgb = p_arbgb. o_sel->t_msgnr_range = s_msgno[]. * * Get the data o_data->get_data( ). * * Generate the ALV o_alv->generate_alv( ).
UML for the demo program
This is how it looks from UML point of view
Advantages
This design definitely needs more effort to achieve than the procedural approach, but this would have more flexibility, less maintenance – reducing overall Total Cost of Ownership (TCO).
- Since the Model class is decoupled from the program it acts independently. It can be plug and play in any different Views
- Separate SEL object makes it free from using the Selection screen parameters and Select Options within the method implementation. This makes it independent than using any selection screen. As long as you set the proper attributes, you would be able use it with DATA class
- Separate View makes it more reusable
Think about developing a WebDynpro using the same logic – Set the proper attributes to SEL object, Instantiate the DATA class and call methods to get the data. Pass the data to WebDynpro View to generate the Output.
OR
Think about exposing this data and use it in bigger application where you need the data to display in an existing complex design – same steps as above which would get you the desired output.
What Do You Think?
Will you be using this design? Do you think its good approach? or Do you think its over kill?
Hi Naimesh,
Thank you for sharing! I like the approach! For those that may complain about effort, this could easily be saved as a pattern that can be used for new developments. Then it is just about adding the code for your particular report. Requiring all new reports to be written this way can also be a good way to start training in OOABAP in organizations that have not yet adopted modern ABAP!
-Brian
Hi,
I am new in ABAP, but this is very helpful for understanding the concept of ABAP OO. However I want to know in real time implementation the ABAPER uses OO concept or Procedural or Both ? Pl reply.
Thanks & Regards.
Hi Naimesh,
your approach looks quite similar to my approach.
I also use a selection-screen Object. Instead of using seperate Attributes for each selection criteria, I use a table which contains all of them. The table is filled as the latest step in event AT SELECTION-SCREEN with function module RS_REFRESH_FROM_SELECTOPTIONS (see description).
I have created two methods to get the selection criteria GET_PARAMETER (only parameter) and GET_PARAMETER_AS_SELOPT (parameter and select-options). You only have to pass the name of the selection criteria. It has the advantage that if the seelction screen changes you do not have to change the selection screen class necessarily.
I also can create “virtual” selection criteria very easy. I have often the requirement to have company code and plant as selection criteria. If plant is not filled it should use all plants which belongs to company code. I can easily add them to the table. The selection in the data class is always based on plant. So treatment in data class is always same.
Instead of using the report as a “controller” I create a controller class. Like you I have a data class and a view class. Additionally I have a message handler class (IF_RECA_MESSAGE_LIST) for Message handling with application log.
Very often I create within the GET_DATA method a Badi GET_DATA. My reports are usually used by more than one client, so changing the data selection is quite easy, if needed. To change selection criterias itself I create an enhancement section within selection screen. I was experimenting with Screen-Badis but they have some disadvantages with report selection screens.
I understand that you will pass(compose) the objects to the place where they are needed. Still undecided about this. For sure it’s one option, but I’m afraid if you have deeper programming levels let’s say 4 or 5 levels. You need to pass them all around.
At the moment I’m planning to create some interface for all this selection-screen stuff and enhance this with additional functionality, e.g. automatic where-clause generation based on selection criterias and some other nice things. We will see how generic all this could be π
So, if you ask me, I don’ think your design is overkill :-))
Best regards,
Tapio
Hi Naimesh,
thanks for the great post!
I would like to add two comments:
I usually go for even more decoupling by not passing the model instance to the view instance when creating the view (as constructor parameter), but by using a separate set_model() method (or as parameter to the generate_alv() method, for example).
The second thing I’d like to point out is the raising of the exception message:
One of the OO design principle is “tell, don’t ask”. In the code above, the exception instance is asked to give all its attributes to the caller. I suggest to implement a raise() method in the exception class (with the same implementation as above). That way, the exception instance can be told to raise itself.
Anyway, please keep up the exceptionally good work!
Regards,
Valentin
Hi Naimesh,
I had to think about your proposed solution again, and smelled a tiny amount of pseudo-OO design in it: The SEL class has two methods: get_default() and validate_message_class(). Both of them only operate on their (IMPORTING) parameters, not on any member attributes. This means that one could turn them into static method without changing anything. What we are left with is a class with only static methods, and member attributes with public visibility – there is basically no difference to a structure variable and two function modules (or form routines).
How can this situation be amended? I propose to implement a set_msg_class(IV_ARBGB) method which includes the validatin check on the importing parameter. If the validation succeeds, the member attribute is set to the passed value. If the validation fails, an exception is raised (just like in the validate_message_class() method). Add a getter method, and for the sake of consistency, add a setter and a getter method for the number range. Add a “AT SELECTION SCREEN on s_msgno” in order to call the setter method there.
Setting the attributes at the beginning of the START-OF-SELECTION can then be dropped.
What are your thoughts on this issue?
Regards,
Valentin
Hi Naimesh,
Nice Blog and Thanks for taking time to explain this π . I do it some what similar except the selection parameter population, I use a method to set the selection params( s_options & para ) via single method as below and store in a variable type as rsds_frange_t. Similarly a get_sel_opt and get_sel_par internally to get the values as below
gr_sel_hdl->set_sel_opt_par( iv_nam = ‘CLEAR’ ).
gr_sel_hdl->set_sel_opt_par( iv_nam = ‘SIMU’ iv_par = p_sim ).
gr_sel_hdl->set_sel_opt_par( iv_nam = ‘CTYPE’ it_sel = so_ctype[] ).
get_sel_par( EXPORTING iv_nam = ‘SIMU’ CHANGING cv_par = lv_testrun ).
get_sel_opt( EXPORTING iv_nam = ‘CTYPE’ CHANGING ct_sel = lt_ctype ).
Hello Naimesh,
Thank you for your very unique and interesting blog.
I’m trying to get better with OO design in ABAP, so my question may a little bit basic : I can see that all your attributes are defined as public so you can access SEL class attributes directly from any other class (and also from the main program). Shouldn’t you use private attibutes and SET/GET methods ?
Thank you for your answer,
Louis-Arnaud
Hello Brian,
Glad you like the post. I have been using this design pattern for a long time in my reports. I try to educate others to leverage the same design, but it would be very unlikely in very big organization.
Thanks,
Naimesh Patel
Hello Prashant N,
I donβt have side-by-side comparison other this series of blogs. But you can read on few Case Studies where I try to capture the advantages of OO ABAP. I would also suggest you read and digest the OO Concepts as well. Good Luck!
Thanks,
Naimesh Patel
Hello Tapio,
Thanks much for a detailed reply. Appreciate you providing glimpse of your design which is similar to my design approach. Definitely, your design is dynamic at least for SEL object.
BADI would be a great way to provide the hooks for the clients to implement their own functionality. But this should similar to Polymorphism which would be achieved by Method redefinition. You could have Factory Method or more specifically Abstract Factory to get the desired object based on specific requirement. The Factory method would instantiate the object for a class where you have redefinition. Have you considered this option?
Regards,
Naimesh Patel
Hello Valentin,
Thanks for your feedback.
I also go for full blown MVC when Iβm working with a complex report functionality, where I would need to produce my data output in different views.
Regarding Exception β Exception ZCX_MSG is being raised with all the necessary information, telling the client what went wrong. The client (report) is catching that. The caller can propagate that exception as error or ignore completely, if they wish. I donβt see a need to have a method RAISE( ) to raise the exception.
Regarding the Selection screen object β I would like to reuse and enhance it for my other reports. I go with Instance methods, even though they seems appropriate for Static methods, as I want to have a flexibility to easily inherit, redefine and use. I would only use Static methods for Instance creation and for pure Utility methods as I described in ABAP Static vs Instance method β Which to use when? .
We can certainly use SETTER and GETTER Methods. I would prefer to have attributes as Public READ-ONLY to remove some of the code clutter. You can use the SETTER method to set the value for the attribute and call that in the AT SELECTION-SCREEN ON . If you go with that approach, you would need AT SELECTION-SCREEN ON for all fields from your selection screen. Few of them might be unnecessary as you wont need to do validation on them. If any of this event gets commented, the fate for that attribute would be untouched.
Regards,
Naimesh Patel
Hello Keshav,
Glad you like the post. Thanks for giving another point of view on how SEL object could be – I would rather go with the SETTER instead of wrapping them in the generic method. As that would provide the kind of documentation of what that method would be doing. Instead of GETTER, I would prefer READ-ONLY.
Thanks,
Naimesh Patel
Hello Louis-Arnaud,
Yes, We can certainly go for SETTER & GETTER methods. I would actually go for GETTER when there is specific need like generating the object if its not there or setting up some default value if its not there. Otherwise, I would prefer the READ-ONLY public attributes.
Thanks,
Naimesh Patel
Hi Naimesh,
Thanks for your input, you are right, read only is correct. Will use it from now on. π
Hi Naimesh,
Sorry for this dumb question, but I’m getting a syntax error in the ZCL_REPORT_T100_DATA->GET_DATA method. The error is ‘The line structure of the table” ME->O_SEL->T_MSGNR_RANGE” is incorrect’.
What type are you using for the T_MSGNR_RANGE attribute in the selection class?
Hello RC,
Sorry for my ignorance. I missed putting the TYPEs for the attributes. I’m glad that you are experimenting with this approach π
The types are now updated in the SEL class implementation section. They are like:
Thanks,
Naimesh Patel
Thanks, but here is my next problem, and if you could help me I would appreciate it. I’m trying to make the move to ABAP Objects after many years of using only procedural styles.
I created all the classes you mentioned above. My ZCL_REPORT_T100_DATA->T_T100 attribute is a public instance attribute of type TT_T100. The GET_DATA method is successfully pulling data from table T100 into the attribute (I can see it if I debug the GET_DATA method). But when I get back to the calling program, the O_DATA instance doesn’t have anything in T_T100…it seems to “lose” the data somehow on the way out of the method, so it passes an empty table into the O_ALV->GENERATE_ALV method.
Do you have any idea of what I may be doing wrong?
Thanks
RC
Hello RC,
What is your Instance name which you have used to call GET_DATA method? You can copy & Paste your code here or send me an email, I would try to find out the error.
Thanks,
Naimesh Patel
Hi Naimesh,
Great approach to implementing reports using ABAP Objects. Here is a scenario I did like some help on.
Lets say I have to make 2 different reports (separate programs).
Report 1 – Extract Sales Orders
Report 2 – Extract Purchase Orders
How can we leverage your solution to use the data, sel, alv classes created in multiple reports. is it method redefinition? or perhaps create the original class as an interface?
Hello SM,
Glad you like the approach. This approach is to reuse the existing logic in multiple similar requirements. If You have a complex logic with quite a few select and processing, you must want to reuse that logic. This approach gives that flexibility to reuse that logic as it is part of the Model class. If you want to leverage the same logic in any other application, you would be able to do that by setting proper values using SEL class and calling the Model class methods.
Certainly, we can create a common interface with generic methods like GET_DATA, GENERATE_OUTPUT which would be implemented in MODEL and VIEW. The interfaces would act as Tag interfaces based on which you can find out which MODEL and VIEW has implemented them.
For your example, Both of the reports would be different from selection screen to output. In that case, if you try to create a Super class and inherit them for both reports, you would harvest any benefits as there would be no common logic between both of them.
Thanks,
Naimesh Patel
Hello Naimesh,
First of all I would like to thank you for providing such a helpful and useful website with very much relevant topics. I have learnt a lot from your blogs. I am working as ABAP Consultant in a manufacturing company, I dont have any senior consultant who can guide me or suggest me about my code efficiency. I generally do R&D on my own and try to grab knowledge as much as I can and try to improve my code. Recently I decided to do everything in OOPs because I really want to work with IT companies and OOPs is in great demand there and I personally want to use OOPs because of its flexibility. But I am not able to decide how to start my program. As I mentioned that I am in manufacturing company so most of the work is about reports(major work), smartforms & bdc (sometimes). I am totally confused about what approach I should follow initially and how to improve myself in OOPs. I can easily use OOPs like procedural programming but its a bad thing which you have mentioned in your earlier blog about initial design. Please suggest me how to improve in OOPs, I am not able to decide how to start in OOPs although I have learnt basics of OOPs by spending approximately One Month. But now how to use them in effective way ? I am eagerly waiting for your reply. Please help me ?
Hello Sameer,
Glad that you want to start using OO ABAP in your day to day developments.
Regarding the REPORTs – This post and the previous part ABAP Object Oriented Approach for Reports β Initial Design would give you jump start on using the OO ABAP.
If you have understood the concepts properly, you would be able to adapt the OO ABAP quickly. The more you practice, you face more issues and you try to fix them. I don’t want to discourage you, but I can’t say, you would be master in one month or so by learning basics. As basics are only the foundation. You would need to build something on top of that using them.
As you start using them, you would realize that if I do it this way or is there any better way, than OO Design Patterns comes into the picture.
Regards,
Naimesh Patel
Hi Naimesh,
Thanks for this blog. I tried this approach and in ZCL_REPORT_T100_DATA , for the method GET_DATA i got a syntax error saying that The IN operator with “ME->O_SEL->T_MSGNR_RANGE” is followed neither by an internal table nor by a value list.
Can you please let me know how to avoid this ?
Thanks,
ram
Hello Ram,
T_MSGNR_RANGE should be a range of the message number. Check on more time, how did you define T_MSGNR_RANGE.
Thanks,
Naimesh Patel
Hi Naimesh,
Thank you very much for the input. Issue got resolved. Looking forward for more valuable posts.
Thanks, Ram
Hi Naimesh, I am new too OOps concept, having 1 confusion regarding range type, where you are passing
types:
tt_MSGNR_Range type range of t100-MSGNR .
I am not getting option of types range of on types tab of class.
Hello Gaurav,
To define the custom Type, Use the menu Go to > Public Section and declare your types there. Once you declare the type, you would be able to see them in the TYPES tab. Once your type is defined, you can navigate to the type from this the TYPES tab.
Thanks,
Naimesh Patel
Can you please explain on old badi and new kernel badi concepts. In google we can find tutorials but i want the basic knowledge what is happening and all that stuff. I think you are the right person to answer. How badi works