The CASE statement in ABAP provides the ability to handle different situations in a clean and organized way.
Regarding structuring, the coding case has a clear advantage over a set of IF statements because a CASE statement reduces the amount of necessary checks and generates well-structured code. However, CASE must be handled with care, otherwise the well-structured method contradicts a well-structure architecture. We’ll walk through various usages of CASE for ABAP development in this blog post.
CASE or IF
The syntax of CASE has two main advantages for clean code over IF statements. While a CASE statement can always be replaced by a set of IF blocks, the opposite is not possible, because a CASE statement only checks a single variable for specific values, whereas in an IF statement, any kind of condition and combined conditions can be checked.
As a result, whenever possible and useful, the CASE statement should be preferred. By design, a CASE statement creates a structure within the method that is easy to follow since every handled case is introduced by a new WHEN block. All comparisons that are made have an implicit positive phrasing since the only operation is a comparison for equality. Thus, you won’t have inverted or other negative forms, which makes it easier to follow the control flow.
CASE statements work especially well with enumerations or similar value sets. They contain a well-defined set of possible cases that define the possible WHEN blocks. Additionally, when the enumeration is extended or a new situation is added to the scope, the CASE can be easily enhanced by adding a new WHEN block. Since this approach is minimally invasive to the other cases, the chance of introducing an error is quite low.
Even though the CASE statement provides a good structure, these statements tend to have more logic within the method than identifying the relevant case and delegating further processing to another method. This scenario contradicts the single-responsibility principle because the method does have more than one reason to change. Changes must be made when another case is added and when the processing of one of the covered cases changes. The listing below shows an example in which the single-responsibility principle has not been considered.
duration = waiting + distance / speed + stops * wait_at_stop.
duration = waiting * 2 + ( distance / speed ).
duration = distance / speed.
In this example, whenever a new vehicle type is added to the scope, the method must be changed. Additionally, when a change is made to the calculation of the duration (e.g., to include slowing down prior to a stop), the method must be changed as well. Thus, more than one reason exists for the change. In summary, a method should hardly do more than the case itself. To consider this principle, each WHEN block should only delegate to another method that implements the calculation, as shown here.
duration = calculate_train_duration( ).
duration = calculate_plane_duration( ).
duration = calculate_car_duration( ).
With the last change, the method is only identifying the relevant cases and delegates the processing accordingly. However, in this example, all our calculations are rather simple; based on the decision processing, the code can get quite complex. Moreover, testing the case method separately from the actual calculation is easier since there would be fewer cases to consider.
CASE or SWITCH
Sometimes, a CASE block is only used to assign a specific field value to a more generic one, perhaps because the type is not relevant anymore or to bring a single value to the user. For this purpose, the SWITCH operation was introduced, which can function as an inline assignment, as shown below.
DATA(id_to_display) = SWITCH string( id_type
WHEN 'EMPLOYEE' THEN partner-employee_id
WHEN 'EXT_WORK' THEN partner-assignment_id
WHEN 'CUSTOMER' THEN partner-customer_id
ELSE THROW unknown_type( ).
Like other operators, SWITCH can also be used inline, for example, for method parameters that allow the result of the SWITCH to be changed. The same rules apply as for other operators. Using the inline options whenever possible might seem convenient at first. Unfortunately, a non-trivial assignment for a parameter can result in code that is hard to follow and maintain.
The previous assignment is not complex and only covers three possible values for the id_type. Still, the statement uses several lines and, when embedded in another method call, is hard to follow. SWITCH can save space and reduce the number of statements when they do not add value. However, you can easily overuse this operator, and in general, you should not use SWITCH within another functional call. Instead, the result should be stored in its own variable, thus enabling you to introduce an additional name.
However, since SWITCH provides a short way to write CASE statements, programmers tend to use SWITCH more than once. For the reasons mentioned in the previous section, reusing the same CASE or SWITCH statements has some drawbacks that you’ll need to consider. An enumeration that is used in a SWITCH can change, and all users will need to be changed. Therefore, the footprint of SWITCH should be reduced so that a change in the enumeration results in a minimum amount of changes in other methods. Extracting the operator within its own method that focuses on identifying the current case and returning the right value results in a better separation, and a change within the enumeration only results in a change in the single switch where the enumeration is encapsulated.
In the end, the same decision must be made: whether having a single method with SWITCH or hiding the different cases in a factory method and creating its own class makes the most sense, which we’ll discuss next.
Multiple Uses of the Same CASE
CASE has the advantage of creating a well-structured method that can be easily followed and often directly reacts to different situations. Unfortunately, due to these advantages, the same CASE is often reused several times. The second example in the CASE or IF section above identifies a vehicle type for which the duration should be calculated in an easy way. It is unlikely that the differentiation based on the type is only relevant for this calculation and not reused in any other method.
Since CASE makes identifying the case so easy, it is often reused several times, thus violating the don’t-repeat-yourself principle. This principle is particularly important to follow because code changes. If copied, rarely are those copies adapted accordingly, and inconsistent behaviors are created.
The repetition of CASE must not be completely identical. Sometimes, only some cases are repeated at another place, which should also be avoided. In an object-oriented environment, the right approach to avoid repetition is polymorphism. Robert C. Martin even suggested hiding CASE statements in the factory method of the class and never repeating it.
The adapted example would relocate the CASE into the factory method, as shown below, which hides the different types in the factory itself. Thus, the conditional is replaced by polymorphism.
instance = new train( ).
instance = new plane( ).
instance = new car( ).
Every subclass of the vehicle class implements the calculate_duration method as required. This can also be done for every other method that has a different behavior other than the upper vehicle class, which contains all coding that is independent from the vehicle type. Hiding the CASE within the factory method helps us follow both the single-responsibility principle and the recommendation to not repeat yourself.
Editor’s note: This post has been adapted from a section of the book Clean ABAP: A Style Guide for Developers by Klaus Haeuptle, Florian Hoffmann, Rodrigo Jordão, Michel Martin, Anagha Ravinarayan, and Kai Westerholz.