Technical Articles
ABAP OO – 3 Simple ways to start using it in your next project
Have you ever asked yourself how you can integrate the object-oriented approach into your next ABAP project?
Being a technology trainer at SAP, customers often ask me how they can use ABAP OO in their next project without creating complex object models and spending hours modeling their application.
Quite often the use case is not so complex that the traditional procedural approach seems more suitable for implementing the application.
This blog post will show you three simple ways and advantages of how you can integrate ABAP OO in your next project!
1. Keep it simple – Bundle your business logic into local classes
Integrating the object-oriented approach into your next ABAP report can be very simple by defining a local class and then implementing the entire business logic within reusable methods.
All you have to do is start by defining a local class with a static method. This will be your entry point after the START-OF-SELECTION event. A simple trick lets you use selection parameters and pass them to the class.
Once you have processed your application data, you can display it using the simple ALV.
In addition to this, you can also take it some steps further by implementing unit tests for critical methods. Unit Tests are a great way to make your application more robust and spin another safety net.
Report z_demo_app.
CLASS lcl_main DEFINITION.
PUBLIC SECTION.
CLASS-DATA: gt_spfli TYPE STANDARD TABLE OF spfli.
CLASS-METHODS:
"! Entry Point
"! @parameter iv_carrid | Corresponding importing parameter for the selection parameter
start IMPORTING iv_carrid TYPE spfli-carrid.
PROTECTED SECTION.
PRIVATE SECTION.
CLASS-METHODS:
"! Fetches application data from DB
select_data,
"! Performs calculation on the application data
calculate.
ENDCLASS.
CLASS lcl_main IMPLEMENTATION.
METHOD start.
" This is your entry point.
" Start implementing your business logic here
select_data( ).
ENDMETHOD.
METHOD calculate.
" Perform calculations
ENDMETHOD.
METHOD select_data.
" Select your data
" Call method
calculate( ).
ENDMETHOD.
ENDCLASS.
** Data definition **)
DATA: go_alv TYPE REF TO cl_salv_table.
** Parameters **)
PARAMETERS: pa_car TYPE spfli-carrid.
START-OF-SELECTION.
" Call static main method and pass through selection parameters
lcl_main=>start( iv_carrid = pa_car ).
" Create SALV with static table
cl_salv_table=>factory(
IMPORTING
r_salv_table = go_alv
CHANGING
t_table = lcl_main=>gt_spfli
).
" Display ALV
go_alv->display( ).
2. Create global classes for system-wide reuse and encapsulation
In many projects, you have to create more complex applications, for instance,
applications with various screens. In this case, the above approach can not be applied.
Apart from that, you will definitely have projects where you have to reuse your coding in different applications. Following the conventional procedural programming approach, you would probably create function pools in order to implement your logic.
This same approach can be transferred to the object-oriented way. In addition, the numerous advantages of ABAP OO are free of charge (unit tests, etc.).
3. TDD (Test-Driven-Development)
Honestly, this is not the simplest approach to start with. The test-driven development is rather a programming philosophy that requires to be applied in your team.
Compared to the first way, you have to think about your test cases for your application before starting to implement your business logic.
Suppose you need to create a method that calculates a critical part of your application. This method should be implemented and delivered as robust as possible. To achieve this, you first need to think about how you can validate that the method calculates the expected output for a given input.
Let me demonstrate this with a simplified example.
CLASS-METHODS:
"! Performs calculation on the application data
calculate IMPORTING iv_num1 TYPE i iv_num2 TYPE i RETURNING VALUE(rv_result) TYPE i.
The static method should calculate the sum of two integer variables.
Based on this, we can make some expectations for the result:
- 1 + 1 = 2
This will be our first test case, so let’s define a test method for it.
CLASS ltlc_app DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS:
for_1_and_1_return_2 FOR TESTING.
ENDCLASS.
Next, we can implement this test case as follows. At this point, we divide our method into three parts – Given, When & Then.
METHOD for_1_and_1_return_2.
" Given
DATA: lo_cut TYPE REF TO lcl_main, " CUT = Class under test
lv_result TYPE i.
CONSTANTS: lc_exp TYPE i VALUE 2. " Expected output
" Create object
lo_cut = NEW #( ).
" When
lv_result = lo_cut->calculate(
iv_num1 = 1
iv_num2 = 1
).
" Then
cl_abap_unit_assert=>assert_equals(
EXPORTING
act = lv_result
exp = lc_exp
msg = 'Your specific error message'
).
ENDMETHOD.
Next, run your Unit Test.
Since we have not yet implemented the method, the unit test has failed. Finally, you can implement the method for the specific use case.
METHOD calculate.
rv_result = 2.
ENDMETHOD.
It is obviously totally wrong. However, this is not the final solution.
Let’s assume you have implemented a more complex method and you run your unit test after you believe it has been implemented correctly.
From an abstract point of view, the method seems to return the correct value for the given parameters. The unit test also tells you that it was successful.
To avoid such a situation in a more complex environment, the best practice is to create additional test cases before implementing your method:
- 1 + 1 = 2
- 2 + 2 = 4
- 9 + 3 = 12
This will ensure that your application is more robust. You can also add negative tests.
So let us implement it correctly:
METHOD calculate.
rv_result = iv_num1 + iv_num2.
ENDMETHOD.
Summary
However, the three basic techniques mentioned above are a good way to start using ABAP-OO in your upcoming projects.
To get started, just copy the code snippets into your IDE and start playing around with them to get more familiar with them.
If you are hungry to learn more about TDD, I can recommend the following free openSAP course:
If you are a beginner in this field, I would also like to encourage you to book a place in one of my courses:
- BC401 – ABAP Objects
- or the online course: BC401E – ABAP Objects
Very good write-up, thank you. I especially liked the part on TDD with the very simple introduction.
Hi Jan,
thank you. I am glad to hear this!
Just a few, possibly unrelated comments.
These are minor quibbles, btw. Overall - good stuff. Thanks.
Hi Matthew,
thanks for your comment!
2. indeed, it is possible to access the parameters directly. But please keep in mind that this is not the best approach if you want to write testable code. In this case I recommend to pass through the parameters or select options. For select options you can define a range table in your class, and then you can refer your importing or changing parameter to the range type.
3. I also encourage and enable everyone in my classes to use the new syntax whenever the opportunity arises and whenever it makes sense. In the above example, this would of course make sense. I will update it. Soon there will be a special blog post about the new syntax. So stay tuned!
4. In many materials and example codes the prefixes are still in use. Especially in many course materials. I use them for consistency reasons. I suppose you have your own internal guidelines.
5. good point. Yes, for complete encapsulation the read-only approach would be better.
6. thank you! I have also planned a special post on this topic
So you’ll use the new syntax whenever you can, but not prefixes… The old syntax is still in use, shouldn’t you stick with that for consistency.
(I know, I'm being very picky... )
I would also applaud any attempt to get people to use OO. ABAP Objects came out in 1999 and it is my hope that one day it may be the norm in ABAP programming.
I regard to the best way to get SELECT-OPTONS (totally non OO things) into classes. I have been struggling with that. If you split your report into INCLUDES (e.g. one TOP for the data, one for class definitions, one for class implentations) then referring to a select-option or parameter directly cause a (false) syntax error i.e. the syntax check gives an error, activation does not.
I have been declaring a local class to store the selection-screen variables and just after START-OF-SELECTION passing the values into an instance which the other classes can read.
I saw an OO program the other day which used that function module which reads all the selection screen values off the screen and then dynamically passed the values to a structure which mirrored what was on the selection-screen. Whichever way you do it you have to declare everything twice and keep them in synch unless you don't use INCLUDES and use direct references to the global variables on the selection-screen.
We are really trying to fit a round peg into a square hole here.The SELECTION-SCREEN concept was designed for procedural programs and so does not play well with OO.
Cheersy Cheers
Paul
I've tried once something like this to represent the selection criteria as a class. But I have to admit that in some reports I handle every single selection criteria per IMPORTING parameter over to the class constructor. Certainly not the best idea...
Also a cool way of doing it. Especially the dynamic fetching of the parameters.
The comparison with the round peg brings it to the point. Select options and parameters were not designed for the object-oriented programming approach. Nevertheless, there are plenty of exciting workarounds out there.
Your way of outsourcing them in a local class is also interesting. Thanks for sharing!
I found also global classes useful to incapsulate code for outputs. You keep the code outside of output and can test it as report and in output to focus on data presentation. Regards. Sergiu.
Just like others, I’m all for encouragement to use OO ABAP. But from my personal experience, I would suggest a slightly different path.
I think starting with global classes is the best option. Most people would start with static methods because they work similarly to function modules. I really like functional calls that make the code look like plain English and this usually doesn’t take a lot of convincing to try in SE24 (which I still find superior to Eclipse ADT, especially for beginners).
After one gets comfortable with static methods (and probably exception handling as part of it), they’d try to apply them in the scenarios where we actually have an object, e.g. a material or a sales order. Some initial mistakes can be made there, which is fine. But any sensible developer will quickly realize that with static method approach we end up repeating lots of stuff, and that could be a “light-bulb moment” to switch to “proper” class.
Because global classes are reusable, it makes it easier to see their benefit. E.g. you invest more time in the initial development but then you get to use it in different places. And it quickly becomes apparent that classes/methods are actually easier to use than FMs, so that usually marks the point of no return.
Only after working up some appetite with global objects, I’d go for a report with a local class. As Paul correctly noted, selection screen concept does not lend itself easily to OO development, by design. So when you start writing such report, the very first step of essentially duplicating all selection screen variables can feel like doing something very stupid. To be honest, in a simplest ALV scenario there are practically no advantages in using OOP vs procedural, thus making it a hard-sell. But if there is any business logic to apply then the benefits of OO approach (like better exception handling, shorter / more readable code, etc.) will be more clear.
This creates sort of a natural learning pattern where you gradually discover advantages from your own work. I know some folks would disagree but personally, I wouldn’t bring up TDD / Unit Test or patterns up to this point because this can easily spook the more conservative developers. And IMHO it’s more important to get everyone into “the function module rehab” first. The rest can be introduced gradually, in baby steps.
But everyone learns differently, of course, so there is not a single “one size fits all” solution here.
We’ve created a global report-with-alv class that contains most events from a standard report (think start-of-selection,..) as well as the required methods for alv handling (display, build field-catalog,..). A new report uses/contains a local class that inherits from the global one, and in most cases the rest of the development is limited to the creation of a ddic structure and a sql statement.
Advantages? A handful developers all write the same code. Which makes it simple, fast and most of all easy to maintain.
Special requirements? Just redefine the relevant methods. And again, when maintaining the code, it suffices to check the local class and you see where it differs from the global one..
And IMHO, only global methods isn’t OO. But, I believe that in this case each of us has to find his/her own way to the ‘aha-erlebnis’. And to get there it’s keep trying and learn from doing.
To be clear, I don't advocate for "only global methods", that wouldn't make any sense. It's just IMHO it's easier to see advantages of global classes than local ones, so they can be a better starting point.
A generic ALV report template is a great option because, as you said, many (most?) developers need to write those and it involves some code that is pretty much boilerplate.
Report OO with select-options and capabilities to display or get data in table.
In class you can define all types and reuse as required without redefinition over and over.
Define the class in SE24 and you can reuse data when needed for other complex report.
Enjoy!
Sergiu
Standard report SE38: AW01N (Asset Explorer) written with forms and classes.