Overview
After showing you the real benefits of using meaningful names, I want to share you another useful tip that can help you to improve the readability of your code. Usually, it's not advisable to use many hard-coded values in your source code, but there are cases when you need them. For example, you want to calculate the total working hours of a month, based on the working days, using the following formula:
total_working_hours = working_days * 8.
We call this number above in the formula, as
Magic Number. Using Magic Numbers can make our life more difficult, because
- we don't have any semantic information about the number 8 (we don't know what it means),
- it can increase the maintenance costs, if we use it many times in our source code,
- it can increase the number of bugs, if we use it many times in our source code, and we make a typo during the changes.
So instead of using the number 8 in our formula, we introduce a new constant, called working_hours_in_a_day with the value 8, and replace all occurrences of the number 8 to this constant in the source code. With this improvement, you make your code more readable, reduce the maintenance costs and reduce the number of the bugs in one fell swoop.
CONSTANTS working_hours_in_a_day TYPE i VALUE 8.
total_working_hours = working_days * working_hours_in_a_day.
Today's message: Be creative, define more meaningful constants!
Last Iteration
As you remember, we have left our code in this state in the last iteration. It's more readable and transparent than before, but it's still hiding some information from us. We still don't now exactly what the magical numbers, 01, 09, 07, CH, TR, or WD means. We can guess, but we cannot be sure, for example the CH means Children or Chemistry. So let's eliminate these misunderstandings using CONSTANTS.
*----------------------------------------------------------------------*
CLASS cl_book_order DEFINITION INHERITING FROM cl_order.
*----------------------------------------------------------------------*
PUBLIC SECTION.
...
PRIVATE SECTION.
...
METHODS calculate_discount_percentage
IMPORTING month_of_sales TYPE string
type_of_book TYPE string
RETURNING value(discount_percentage) TYPE i.
ENDCLASS. "cl_book_order DEFINITION
*----------------------------------------------------------------------*
CLASS cl_book_order IMPLEMENTATION.
*----------------------------------------------------------------------*
...
METHOD calculate_discount_percentage.
IF month_of_sales = '01' AND type_of_book = 'CH'.
discount_percentage = 30.
ENDIF.
IF month_of_sales = '09' AND type_of_book = 'TR'.
discount_percentage = 15.
ENDIF.
IF month_of_sales = '07' AND type_of_book = 'WD'.
discount_percentage = 5.
ENDIF.
ENDMETHOD. "calculate_discount_percentage
...
ENDCLASS. "cl_book_order IMPLEMENTATION
Define Constants Globally in the Parent Class
There are many ways defining constants in ABAP, and specially in OO ABAP. Both approaches are good, it only depends on the requirements. We can define them:
- in a global class,
- in a global interface,
- in the parent class,
- within the same class,
- and so on.
What if I say that the magic numbers 01, 09 and 07 are represent months? Alright, you can say that it's not a big deal. But if you have the tools to type January, September and July instead of those magic numbers, why wouldn't you do that?
Speaking about our example code, we are going to define these constants in the parent class, under the PROTECTED SECTION, in order to share them among all the inherited classes (since it' not only book relevant information).
Then there is nothing left, than referring them as january, september and july in the subclass, cl_book_order. If the month of the sales date is equal to january... I'm a big fan of code like this, it's self-expressive!
*----------------------------------------------------------------------*
CLASS cl_order DEFINITION.
*----------------------------------------------------------------------*
PROTECTED SECTION.
CONSTANTS january TYPE string VALUE '01'.
CONSTANTS july TYPE string VALUE '07'.
CONSTANTS september TYPE string VALUE '09'.
...
ENDCLASS. "cl_order DEFINITION
*----------------------------------------------------------------------*
CLASS cl_book_order DEFINITION INHERITING FROM cl_order.
*----------------------------------------------------------------------*
PUBLIC SECTION.
...
PRIVATE SECTION.
...
METHODS calculate_discount_percentage
IMPORTING month_of_sales TYPE string
type_of_book TYPE string
RETURNING value(discount_percentage) TYPE i.
ENDCLASS. "cl_book_order DEFINITION
*----------------------------------------------------------------------*
CLASS cl_book_order IMPLEMENTATION.
*----------------------------------------------------------------------*
...
METHOD calculate_discount_percentage.
IF month_of_sales = january AND type_of_book = 'CH'.
discount_percentage = 30.
ENDIF.
IF month_of_sales = september AND type_of_book = 'TR'.
discount_percentage = 15.
ENDIF.
IF month_of_sales = july AND type_of_book = 'WD'.
discount_percentage = 5.
ENDIF.
ENDMETHOD. "calculate_discount_percentage
...
ENDCLASS. "cl_book_order IMPLEMENTATION
Define Constants within the Class
What about the book types? What if I say that the magic numbers CH, TR, WD mean each of the followings: Children, Travel and Web Design?
Now, we are talking about book type that is a book relevant information, so we can simply define them within the same class, cl_book_order, and referring them in the implementation section as Children, Travel and Web Design.
Alright, the first branch sounds like this: if the month of the sales date is equal to january, and the type of the book is equal to children, then discount percentage is going to be 30. What do you think? Is it better? No explanation, no head scratching!
*----------------------------------------------------------------------*
CLASS cl_order DEFINITION.
*----------------------------------------------------------------------*
PROTECTED SECTION.
CONSTANTS january TYPE string VALUE '01'.
CONSTANTS july TYPE string VALUE '07'.
CONSTANTS september TYPE string VALUE '09'.
...
ENDCLASS. "cl_order DEFINITION
*----------------------------------------------------------------------*
CLASS cl_book_order DEFINITION INHERITING FROM cl_order.
*----------------------------------------------------------------------*
PUBLIC SECTION.
...
PRIVATE SECTION.
CONSTANTS children TYPE string VALUE 'CH'.
CONSTANTS travel TYPE string VALUE 'TR'.
CONSTANTS web_design TYPE string VALUE 'WD'.
...
METHODS calculate_discount_percentage
IMPORTING month_of_sales TYPE string
type_of_book TYPE string
RETURNING value(discount_percentage) TYPE i.
ENDCLASS. "cl_book_order DEFINITION
*----------------------------------------------------------------------*
CLASS cl_book_order IMPLEMENTATION.
*----------------------------------------------------------------------*
...
METHOD calculate_discount_percentage.
IF month_of_sales = january AND type_of_book = children.
discount_percentage = 30.
ENDIF.
IF month_of_sales = september AND type_of_book = travel.
discount_percentage = 15.
ENDIF.
IF month_of_sales = july AND type_of_book = web_design.
discount_percentage = 5.
ENDIF.
ENDMETHOD. "calculate_discount_percentage
...
ENDCLASS. "cl_book_order IMPLEMENTATION
Summary
I think I don't have to say anything, since improving the readability using meaningful names and constants speaks for itself. I only want to show you the before & after code snippets. Feel the differences!
Before the improvements
...
*----------------------------------------------------------------------*
CLASS cl_ord1 IMPLEMENTATION.
*----------------------------------------------------------------------*
...
METHOD calc_dp.
IF ms = '01' AND tb = 'CH'.
dp = 30.
ENDIF.
IF ms = '09' AND tb = 'TR'.
dp = 15.
ENDIF.
IF ms = '07' AND tb = 'WD'.
dp = 5.
ENDIF.
ENDMETHOD. "calc_dp
...
ENDCLASS. "cl_ord1 IMPLEMENTATION
After the improvements
...
*----------------------------------------------------------------------*
CLASS cl_book_order IMPLEMENTATION.
*----------------------------------------------------------------------*
...
METHOD calculate_discount_percentage.
IF month_of_sales = january AND type_of_book = children.
discount_percentage = 30.
ENDIF.
IF month_of_sales = september AND type_of_book = travel.
discount_percentage = 15.
ENDIF.
IF month_of_sales = july AND type_of_book = web_design.
discount_percentage = 5.
ENDIF.
ENDMETHOD. "calculate_discount_percentage
...
ENDCLASS. "cl_book_order IMPLEMENTATION
Outlook
If you want to learn more about code best practices in ABAP, then I highly recommend to watch my colleague, Laszlo's Pluralsight course: Introduction to ABAP for SAP Business Warehouse Developers.
At last, apply Martin Fowler's approach during your development, and read his book also (one of the best articles):
"Create a constant, name it after the meaning, and replace the number with it."
Refactoring: Improving the Design of Existing Code
Authors: Martin Fowler with contributions by Kent Beck, John Brant, William Opdyke, Don Roberts
(Addison-Wesley Object Technology Series).