Can you read ABAP code and understand its aim? You will expect some variation of a given pattern if you understand the solution. And if you have experience writing code in the domain you will look for idioms and you will be well aware of some pitfalls the implementation may have avoided. From your experience, you can tell whether the code is best practice or not.
But how do you teach this knowledge, how do you give advice to the original developer (my younger self), how do you teach experience? While discussing with my younger self, I would avoid saying this is good, that is bad mojo, but saying it helps me in this context and it causes trouble in this other context would let me avoid religious war. The main point here is to provide the context. This is how patterns are written. I would like to contribute to a pattern language for ABAP refactoring.
A pattern language is developed to pass knowledge. We try to say: I had this problem, I used this solution because it helped me in this context. With this information in the open, another voice can say: this lead to trouble is this other context. A well written pattern is an enlightening discussion of contexts for solution to a given problem.
Smart UI vs. MVC
Eric Evans uses the term Smart UI pattern (cf. Domain Driven Design, Chapter 4: Isolating the Domain - The Smart UI Anti Pattern) to describes the less sophisticated but wildly successful method of adding logic to the UI code. The rest of the his book advocates Domain Driven Design with a Layered Architecture approach (e.g. Model View Controller MVC) that will reduce complexity in a large project. The point is made that additional complexity and the costs of learning object modeling can make the SmartUI a pragmatic move for a given team.
So If the circumstance warrant: Put all the business logic into the user interface. Further chop the application in small functions and implement them as separate user interfaces. Use the most automated UI building tools available.
Advantage: Productivity is high and immediate for simple applications.
I want to stress the importance of the context here: even if you are shunning the Smart UI Pattern, let us recognize it is highly successful in some contexts. The problem is that the Smart UI is an Anti-Pattern in the context of layered architecture that is the prefered pattern for larger systems. So a pattern description does not just say this is good, this is bad. It tries provide context so that one can get an insight about the limits of a given method.
Category: Objects Discovery for ABAP Reports
ABAP Report Model View Controller
Name: ABAP Report Model View Controller
Problem: We have a data-driven report in Input / Processing / Output style and want to convert it to objects. How shoud an ABAP report be structured? How can we partition a report in an object oriented way?
Approach: The general design pattern is layered architecture. We define distinct layers which interact only through defined interfaces with each other. The most popular pattern is MVC.
How it works: Create an application class that will be the CONTROLLER. It has an entry point for the program that will be called at the START-OF-SELECTION event and other possibly static method of events like AT-SELECTION-SCREEN-OUTPUT.
Create an view class that will be used to implement the output, e.g. ALV.
Create a model class that will contains the business logic, e.g. how to retrieve the data.
When to use it: while converting classical reports to ABAP Objects. While refactoring existing reports.
ABAP Report Parameter Object
The suggested refactoring process for ABAP reports resonates with me. For some time now I have been extracting selection screen parameters into an object as one of the first steps. If anyone else has done the same, then I would suggest to elevate this practice to a pattern:
Name: ABAP Report Parameter Object
Problem: How do we handle the global selection screen parameters in an ABAP Objects program?
Approach: We create a new parameter object. The parameter object is passed to all routines that need access to the global variables. It can be generated with specific values in unit tests.
How it works: Create a class with a private method GET_SELECTION( ) that is called in the constructor. With this, a VALUE OBJECT is created with all current selection parameters as member variables as public read-only members of the class.
When to use it: while converting classical reports to ABAP Objects
Further Notes: The parameter object is a dumb object (data only) with minimal behavior. After refactoring, more behavior could be moved to its class.
If we have this selection-screen definition:
SELECT-OPTIONS s_class FOR vseoclass-clsname.
PARAMETERS: p_dtls AS CHECKBOX DEFAULT 'X',
p_subco AS CHECKBOX.
then we can create this class
TYPES tt_r_clsname TYPE RANGE OF vseoclass-clsname.
CLASS lcl_param DEFINITION.
DATA r_clsname TYPE tt_r_clsname READ-ONLY.
DATA dtls TYPE flag READ-ONLY.
DATA subco TYPE flag READ-ONLY.
CLASS lcl_param IMPLEMENTATION.
r_clsname = s_class.
dtls = p_dtls.
subco = p_subco.
If the developer does not use the global program variables, they can be renamed/removed later.
The intent is to remove any reference to global selection variables in the code an replace it by access to an attribute of the PARAMETER object. The attribute are READ-ONLY to avoid having them changed everywhere. The PARAMETER class can offer friendship to a test class to support test data creation. I think this creates a path for further refactoring.
I usually define a CONTROLLER class, that interacts with a MODEL and a VIEW class. The controller passes the PARAMETER object to all other classes.
ABAP Report User Object
In ABAP reports we see authorization checks all over the place, but they are usually not well tested. I rember the menacing words of a (friendly) colleague asking who this report was for and which roles were needed. And I remember how often my code failed to perform correctly in production because some parts where not being executed due to missing authorization, so I suggest this practice:
Name: ABAP Report User Object
Problem: How do make sure the all authorization checks are correctly handled without creating a set of test users specific for each report?
Approach: We create a new user object. The user is passed to all routines that need authorization checks. It can be replaced by mock users in unit tests.
How it works: Create a user class and move all authorization checks as public methods of the user class. The CONTROLLER class can create an object of this class and pass it around
When to use it: while converting classical reports to ABAP Objects, while refactoring ABAP reports
ABAP Report Log Object
Name: ABAP Report Log Object
Problem: How do we collect all errors and messages in an application? Can we avoid passing a message table parameter to all routines?
Approach: We create a new log object. The log object could be initialized with a parameter object. It is passed to all routines who can generate errors/messages.
How it works: Create a class with a private attribute MT_MESSAGES and some public method that can add system messages, exceptions and other messages to the message log. For system messages, the MESSAGE statement is executed with an INTO variant and the system variables SY-MSGV1 to SY-MSGV4 and other SY-MSGTY are evaluated.
When to use it: while converting classical reports to ABAP Objects, while refactoring.
Further Notes: The log object can include additional behavior like displaying the list of messages. But it should be foremost a message collector. Adding too much behavior here could hide application specific objects that should be extracted to a new class.
There are 3 other patterns I would like to discuss so there could will be a next episode
- Using RAISING parameter to propagate ABAP Objects Exceptions instead of Error code.
- The NULL Object pattern (example)
- The iterator pattern (ALV )
but the general idea ist that the pattern language is a good way to present concepts and the discussion is part of the pattern.
Interested in discussing Patterns with us?
Please follow up on our career page.