In this article I would like to discuss some of my thoughts and conclusions you can find useful and implement in BAdI. I will point out things I worked out and noticed during my work. I would also like to encourage you to share your thoughts in the comment section below.
It’s an object
I have seen many BADi implementations and many times I had an impression that the only thing people do is copy-paste code from user exits and wrap it with methods in classes. Let’s not forget BAdI is an interface implemented in class, so we can take full advantage of ABAP OO!
CONSTRUCTORs and class attributes
Lots of our enhancements need to reach database to fetch some additional data or our custom Z-tables with mapping. Let’s imagine that our implemented method is called per each record in a loop (which is quite common). If you implement select statement in this method you will perform multiple calls which will cause unnecessary traffic between application server and database. When you know that table you are going to use is not enormous – select data in class constructor/constructor only once and perform reads only.
CLASS zcl_implementation DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES if_badi_interface . CLASS-METHODS class_constructor . PROTECTED SECTION. PRIVATE SECTION. CLASS-DATA: _mt_ztable TYPE TABLE OF ztable WITH KEY zkey. ENDCLASS. CLASS zcl_implementation IMPLEMENTATION. METHOD class_constructor. SELECT * FROM ztable INTO TABLE _mt_ztable. ENDMETHOD. METHOD if_badi_interface~process_line. ASSIGN _mt_ztable[ zkey = iv_key ] TO FIELD-SYMBOL(<ls_ztable>). IF sy-subrc = 0. ... ENDIF. ENDMETHOD. ENDCLASS.
In case of class constructor _mt_ztable will be accessible for all instances of your class in program run, so database traffic is significantly decreased. You could also split this data in instance constructor.
What if table is really big and reading it to internal memory is not an option? You can buffer it in attributes:
METHOD if_badi_interface~process_line. ASSIGN _mt_ztable[ zkey = iv_key ] TO FIELD-SYMBOL(<ls_ztable>). IF sy-subrc = 0. ... ELSE. SELECT SINGLE * FROM ztable INTO @DATA(ls_ztable) WHERE zkey = @iv_key. IF sy-subrc = 0. ... APPEND ls_ztable TO _mt_ztable. ENDIF. ENDIF. ENDMETHOD.
Many interfaces have INIT method. In many cases this is also a good place to fetch your mapping data.
This is really obvious, but we’ve all seen too many unreadable methods with hundreds of lines of code. Wrap your code with methods! It is much easier to do in class than in standard user exits. You can create as many private methods as you want and hide some complex logic there. It is even more useful when you need the same logic across different methods of your BADi implementetion.
In classic user exits when there were multiple different cases you were doomed for CASE – ENDCASE or lots of IFs. If BADi can be filtered – use it. You can implement different classes for different cases which separates logic and makes code easier to read and maintain. If you create classes for different cases with different mappings etc. you can use instance constructor to keep relevant data as attribute.
New ABAP 7.52 syntax
In many, many cases signatures of methods contain internal tables. In the old days in order to fetch data from database you would have to use SELECT SINGLE inside a loop or use FOR ALL ENTRIES. With ABAP 7.52 it is possible to perform selects from internal table which has huge utility in our case.
This great blog gives an insight into performance of two statements: https://blogs.sap.com/2017/11/13/the-performance-of-open-sql-as-in-abap-7.52/
Official documentation: https://help.sap.com/doc/abapdocu_752_index_htm/7.52/en-US/abapselect_itab.htm
Important point to note – you can use only one internal table in a select statement. What’s more, internal table cannot have a header line.
What are your thoughts?
The above are my ideas how to implement BADis more effectively and smoothly. What are yours? Do you know some nice practices and tricks?