You are on page 1of 65

For SAP internal use

Page |1

ABAP OO for HCM


Authors: Gabriel Henrich and Eduardo Balle.

1 Contents
2 3

Version history ........................................................................................................................................... 3 Introduction ................................................................................................................................................ 4 Motivation .......................................................................................................................................... 6 Misusing abap-objects....................................................................................................................... 7 Further reading on object orientation ............................................................................................. 8 Notes .......................................................................................................................................... 8

3.1 3.2 3.3 4 5 6 7 8 9 10 10.1 10.2 11 12 12.1 12.2 12.3 13 14 14.1 15 15.1 16 16.1 16.2

3.3.1

Procedural vs. OO in HCM ......................................................................................................................... 9 Use UML Class diagrams for designing ................................................................................................... 10 Test the business logic implementing Unit tests ................................................................................ 10 Separation of concerns ............................................................................................................................ 11 GLOBAL objects VS local objects ............................................................................................................. 12 Local classes in reports ............................................................................................................................ 12 Class pools in global classes ................................................................................................................ 13 Design classes with a reasonable size ............................................................................................ 14 Use Local Classes to Modularize Class Pools ................................................................................. 14 Designing classes with correct data encapsulation ........................................................................... 15 Memory usage ..................................................................................................................................... 15 Whenever possible, use Lazy Instantiation.................................................................................... 16 Do not modify global data in methods........................................................................................... 17 Access Attributes in Methods via Selectors ................................................................................... 17 Inheritance and polymorphism........................................................................................................... 18 Static methods vs. Singleton ............................................................................................................... 20 Related guidelines............................................................................................................................ 20 Interface composition.......................................................................................................................... 22 Additional guidelines on interfaces ................................................................................................ 23 Use Inheritance Moderately ................................................................................................... 24 Data Types within classes and method parameters.......................................................................... 24 Do not use obsolete Parameter Kinds ............................................................................................ 26 Do not use obsolete Parameter Typings ........................................................................................ 27 15.1.1

For SAP internal use


Page |2 16.3 17 18 19 20 20.1 21 21.1 21.2 21.3 21.4 21.5 21.6 Type Parameters as Specific as Possible ........................................................................................ 27 Local types and implementation includes of global classes ...................................................... 28 Global class with multiple implementations...................................................................................... 30 Using events ......................................................................................................................................... 34 Friend classes ....................................................................................................................................... 35 How to reuse local objects outside the global class...................................................................... 37 Case study............................................................................................................................................. 38 Case study requirements: ............................................................................................................... 38 Solution............................................................................................................................................. 38 Application log implementation: .................................................................................................... 39 Tax on wagetypes ............................................................................................................................ 39 Workflow: ......................................................................................................................................... 39 Result of workflow ........................................................................................................................... 40 Class diagram ........................................................................................................................... 40 Source code of classes ............................................................................................................ 41 Payroll function source code ............................................................................................... 46

21.6.1 21.6.2 21.6.3 22 22.1 22.2 22.3 22.4 22.5 22.6 22.7 22.8 22.9 22.10 22.11 22.12 22.13 22.14 23 23.1

Additional guidelines ........................................................................................................................... 48 Coding style ...................................................................................................................................... 48 ABAP Objects, Modularization, and Program Flow ....................................................................... 50 Some General Hints on Modularization ......................................................................................... 50 Declaration of the Instance Constructor ........................................................................................ 52 Control Structures............................................................................................................................ 54 Handle Exporting Parameters Passed by Reference Appropriately ............................................. 55 Jump Out of Methods Only via RETURN ........................................................................................ 56 Do not use CALL METHOD for static invoke................................................................................... 57 Type and Data Declarations ............................................................................................................ 57 Reuse Only Data Types that Exactly Match Your Needs ........................................................... 58 Use the data type ABAP_BOOL as Boolean data type .............................................................. 59 Declare Data Objects Only in Classes ......................................................................................... 59 Use the TABLES Statement Only for Dynpro Structures ........................................................... 60 Refer to Data Objects when Appropriate, refer to Data Types Otherwise ............................. 60

Error Handling ...................................................................................................................................... 61 Exceptions................................................................................................................................................. 61 Assertions ......................................................................................................................................... 64

For SAP internal use


Page |3 23.2 Messages .......................................................................................................................................... 65

2 Version history
Version 1, July 2011: Released Version 2, August 2011: a. Review of chapter Global class with multiple implementations b. Review of chapter singleton vs static methods with code example c. New chapter local types / implementation includes of global classes

For SAP internal use


Page |4

3 Introduction
This is a guide to assist on designing new HCM applications or enhancing existing functionalities based on object oriented programming. This guide was built based on recommendations from SAP development guidelines document and on the article State-of-the-Art ABAP1 in order to support best practices on software development between HCM teams. This guide is also helpful on solving common issues and even bad practices on application development. Currently the great majority of HCM applications are implemented based on procedural programming style mainly by reading and filling internal tables in order to achieve a specific result. There are some reasons why the procedural programming is so common among HCM applications, for example: The great majority of HCM applications are processes which must be executed in batch for a large number of employees resulting on minimum user interaction; the reports are schedule to run in background mode, results are stored in database or files, logs are printed to system spools. In many cases, the online execution serves just for testing purposes. The main user interaction application (practically the only one in HCM) is the employee masterdata transaction (PA30, PA40, etc), and is built on a module pool framework. In any case, the maintenance and new developments on this area are not so frequent or not so problematic. Most of the HCM applications and foundations were developed when the only abap programming model available was the procedural one. In most cases, the query between employee masterdata, results and customizing tables is the necessary procedure to achieve a reporting result. Current issues of procedural style: Solution design o When developing a new business rule or report the developer is guided by step-by-step internal table operations (looping at this table, what has to be done for each entry?). This leads to the basic recipe: concatenate some internal table loops until a certain level, make some IFs or CASEs, and finally handle each special case with a procedure. o At the end, the number of similar or redundant FORMS (procedures) is big. Maintenance o When a report is fresh new, the code looks clean and simple. One year later it is filled with IFs, ELSEIFs and CASES o The procedural style usually leads to the creation of long reports full of global data; when performing a correction, it is necessary to study the practically all implementation in order to ensure that this correction will not influence - or be influenced by - other procedures managing the same global data. o Since there are several FORM routines with similar code within a report, you cannot forget to check if your correction needs to be replicated in similar Forms Readability

State-of-the-Art ABAP: Guidelines for Writing Robust, Understandable, and Maintainable ABAP Programs. Andreas Blumenthal, Horst Keller - SAP NetWeaver Application Server ABAP Preprint from a series of articles published in three subsequent issues of the SAP Professional Journal, starting January/February 2006

For SAP internal use


Page |5 o Usually the business rules for HCM are long and full of special cases. It is then difficult to understand the basics of a report/function, as per the following reasons: Reports are very long; no distinction between handling special cases from general cases; When the FORM procedures are not very long, usually the call stack gets very deep.

What are the advantages of the OOP in respect to the issues of procedural style? Here are a few of them: Solution design o The development of a business rule or report is guided by modeling objects; these objects can be reused between different applications. o Class diagrams are a visual part of the design document, it facilitates the understand of the functionality Maintenance o The general solution model will remain stable. Specific case changes are minor compared to the solution model o Correcting a general functionality on the super class will automatically correct all child classes. Readability o Functionality modeling is simpler o the purpose of the functionality is evident due to its public interface(methods) o Combination between object abstraction and object polymorphism makes the purpose understanding much more easy and simple, especially for developers used with other contemporary development languages (java, C++, C#, etc...)

Analogy between procedural and object oriented programming

For SAP internal use


Page |6

3.1 Motivation
This is chapter has been extracted from the article state-of-the-art-ABAP and serves as a motivation for the usage of object-oriented programming: ABAP supports a hybrid programming model. You can use an object-oriented (OO) programming model based on classes and interfaces, and you can use the more classic procedural and event-driven programming model based on function modules, subroutines, dialog modules, and event blocks. Both models can be used in parallel. You can use classes inside classic processing blocks or you can call classic procedures from methods. In ABAP Objects, SAP has implemented a cleanup of the ABAP language. Within the scope of this language cleanup, stricter syntax checks are performed in classes that restrict the usage of obsolete language elements. The stricter syntax checks usually result in a syntax which should also be used outside of ABAP Objects but where the old versions could not be forbidden for compatibility reasons. The stricter syntax check comprise Prohibiting many obsolete statements and additions Requiring many implicit syntax completions to be explicit Detecting and preventing potentially incorrect data handling You can find a complete list of all obsolete language elements that are forbidden in ABAP Objects in the ABAP keyword documentation.

Recommendation

The object-oriented programming model provided by ABAP Objects is superior to the procedural programming model, since it offers better means for 1. Data encapsulation 2. Explicit object instantiation 3. Improved code reuse via inheritance 4. Standalone interfaces 5. Explicit event raising and handling In addition to these general reasons for using ABAP Objects, three more specific reasons apply:2 1. ABAP Objects is simpler to learn and use than procedural and event-driven ABAP 2. ABAP Objects has a stricter syntax check that prevents the use of obsolete language elements

You can find a detailed description of these eight reasons and a comparison of ABAP Objects to procedural ABAP in Not Yet Using ABAP Objects? Eight Reasons Why Every ABAP Programmer Should Give it a Second Look (SAP Professional Journal, September/October 2004). As stated in that article, we believe that programming with ABAP Objects means programming in a modern style that exploits the benefits of a paradigm that was invented to solve the problems of complex software projects object orientation.

For SAP internal use


Page |7 3. ABAP Objects provides access to new ABAP technology Therefore we recommend as our first and most fundamental guideline the use of ABAP Objects wherever possible. This means that all coding should be implemented in global or local classes and that global or local interfaces should be used when appropriate. Exceptions to this rule include the reuse of services that are implemented in existing function modules or the implementation of new function modules where technically necessary (you still must create function modules for remote invocations via RFC and we recommend function modules for handling classic dynpros defined in function groups). Nevertheless, all operational statements3 relevant for an application should be implemented in methods. As a blanket rule, subroutines (FORM ENDFORM) should never be used for implementing new operational statements. Instead, for internal services, local classes are to be used. This holds especially also for the internal modularization of class pools4, where methods of local classes are preferable to private methods of the global class itself.

3.2 Misusing abap-objects


Another point is that many experienced abap procedural application developers have migrated to abap objects application development by simply replacing the concept of FORM with Methods, thus not really an OO approach. Here follows an example, extracted from the internet, where the author mentions a common wrong usage of the abap objects:
CLASS lcl_create_ztabledata DEFINITION. PUBLIC SECTION. DATA: some TYPE TABLE OF ztable, variables TYPE ztable, used TYPE kunnr, as TYPE adrnr, globals TYPE adrnr. METHODS: check. METHODS: get_more_data. METHODS: update_ztable. ENDCLASS.

Its very bad class design. In fact, its a procedural program wrapped into a local class, which otherwise would have looked like this:
DATA: some TYPE TABLE OF ztable, variables TYPE ztable, used TYPE kunnr, as TYPE adrnr, globals TYPE adrnr.

With operational statements we denote all coding that is not declarative. Operational statements comprise all statements that can be executed. In ABAP, an operational statement is always part of the implementation of a processing block. 4 A class pool is an ABAP program that contains the definition of a single global class and can contain an arbitrary number of local classes. The class pool of a global class is generated automatically from the Class Builder tool of the ABAP Workbench when the class is created. The counterpart of class pools for global interfaces is interface pools.

For SAP internal use


Page |8
FORM check. ENDFORM. FORM get_more_data. ENDFORM. FORM update_ztable. ENDFORM.

But thats beside the point (I even found a worse piece of code where everything was defined as statics, which actually seems to be a quite common practice among ABAP programmers who are told to use OO but dont understand one bit of it). Anyway, the point is, I was working on another piece of code which required the functionality written inside the check method. But because it is written inside a local class, I could not access this functionality. It was not reusable. If this program would have been written with a decent OO design and global classes, I would have been able to use a domain or application service class to perform the check.

3.3 Further reading on object orientation


There are many books about object orientation, object-oriented programming languages, objectoriented analysis and design, project management for OO projects, patterns and frameworks, and so on. This is a small selection of good books covering the most important topics: Scott Ambler, The Object Primer, SIGS Books & Multimedia (1996), ISBN: 1884842178 A very good introduction to object orientation for programmers. It provides comprehensive explanations of all essential OO concepts, and contains a procedure model for learning OO quickly and thoroughly. It is easy to read and practical, but still theoretically-founded. Grady Booch, Object Solutions: Managing the Object-Oriented Project, Addison-Wesley Pub Co (1995), ISBN: 0805305947 A good book about all of the non-technical aspects of OO that are equally important for effective object-oriented programming. Easy to read and full of practical tips. Martin Fowler, UML Distilled: Applying the Standard Object Modeling Language, AddisonWesley Pub Co (1997), ISBN: 0201325632 An excellent book about UML (Unified Modeling Language - the new standardized OO language and notation for modeling). Assumes knowledge and experience of object orientation. Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides, Design Patterns. Elements of Reusable Object-Oriented Software, Addison-Wesley Pub Co (1998), ISBN: 0201634988 Provides a pattern, showing how recurring design problems can be solved using objects. This is the first big pattern book, containing many examples of good OO design. James Rumbaugh, OMT Insights: Perspectives on Modeling from the Journal of ObjectOriented Programming, Prentice Hall (1996), ISBN: 0138469652 A collection of articles addressing the many questions and problems of OO analysis and design, implementation, dependency management, and so on. Highly recommended. 3.3.1 Notes If you are new to object-orientation, you should read Scott Amblers The Object Primer and then acquire some practical experience of your own. You should definitely use the CRC techniques

For SAP internal use


Page |9 described by Ambler and Fowler for object-oriented analysis and design. After this, you should learn UML, since this is the universal OO analysis and design notation. Finally, you should read at least one book about patterns. At the beginning of a large OO project, the question immediately arises as to the sequence in which things should be done, which phases should be finished at what time, how to divide up and organize the development work, how to minimize risks, how to assemble a good team, and so on and so forth. Many of the best practices of project management have had to be redefined for the object-oriented world, and the opportunities that this has produced are significant. For further information about how to use OO in project management, see Grady Broochs book Object solutions, or the chapter entitles An outline development process from Martin Fowlers book. There are, of course, many other good books about object orientation. The above list does not claim either to be complete, or necessarily to recommend the best books available.

4 Procedural vs. OO in HCM


It is difficult and closetful to adapt existing legacy functionality and transform it into objects, however new functionality branches or new applications should be designed and implemented as objects. As per SAP standard guidelines, the creation of new FORMs and Function Modules are to be avoided (except for very specific cases, see guidelines). Of course, the creation of FORMS are actually still a good practice for maintaining corrections of legal functions, where the existing programming and coding style should be kept. For new functionalities, the business rules must be designed and implemented as objects; it must be avoided the usage of procedural programming (and therefore, no FORMs and no Function modules). When designing functionalities, it must be foreseen where these objects might be reused, it is very common for example, that a set of data is needful both in a payroll function and in legal reports. Nowadays, the usual practice of reusing procedures on different places is to either use a function module or to copy and paste a FORM (or even PERFORM IN PROGRAM!). The copy and paste of FORMS is evidently harmful, as for the usage of Functions require both the complete instantiation of the function group into the sessions memory and enforces the procedural programming style; the same is valid for PERFORM IN PROGRAM. The use of Function modules for code reuse is obsolete. The reason is simple: a function that has to support a different number of scenarios or behaviors is required to expose a big parameter interface (many of them as optional) and treat all cases internally chaining CASES and IFs. Maintenance on functions easily creates side effects on other functionalities out of the maintenances scope. New classic reports - according to SAP standard guidelines -should be avoided. The recommendation is to build transactions calling methods directly; selection screens are - since a long time - obsolete; Dynpros should be created on function groups and all business logic implemented as objects using MVC concept. Exceptions for these cases are for reports which must be also run in background mode (the great part of the HCM reports) or reports which use a complex logical database (the HCM case).

For SAP internal use


P a g e | 10 Currently it is not possible to afford ignoring (rewriting) the functionalities that PNP offers: selection screen framework with HR reporting class, authorization control and selection of employee master data. Therefore, the recommendations are: New reports for HCM with selection screens are allowed. Whenever necessary a new report, use it just as a container for the object oriented functionality Do not create new includes! Do not create new function groups (only for maintenance views and very special cases, like RFC connection) In any case, the creation of INCLUDES and sharing includes is to be forbidden. Modularization must follow OO paradigm.

5 Use UML Class diagrams for designing


Class diagrams are a very useful design tool. It facilitates the creation of design documents and, most important, facilitates the comprehension of the solution by other colleagues. In many cases the class diagram can serve as the detailed design part of the design document. This practice also facilitates the implementation phase where a developer can create the definition (and not the implementation) of the objects following the class diagram, and as soon as the definitions are created, the unit tests can be written. The real implementation of methods can be left as the last step (test driven development).

6 Test the business logic implementing Unit tests


Unit tests are the fastest way to create automated test and also serve as regression tests since the execution of these are automatically integrated with the Checkman tool on test and consolidation systems. SAP wants to increase the current utilization of unit tests within the standard software (nowadays the Ecatts are the main automated test strategy). There are some hints and guidelines on the development of unit tests as follows: The unit tests should not depend on customer customizing (tables type C, or features) The unit tests should not depend on masterdata nor application table entries You can build a unit test class that inherits or is defined as a friend of the object you want to test, in this way you can overwrite/bypass/simulate the real database interaction of the object under testing In case you need a set of internal tables for use in the test case, you can use a test data container instead of filling the internal tables with hardcoded entries. Unit tests are the basis for test driven development, where you implement the tests before the project implementation. Unit tests help you to protect your code against future maintenance thus avoiding bugs!

For SAP internal use


P a g e | 11

7 Separation of concerns
The business logic should be separated from presentation or logging functionality. The aim is to increase the reuse of business logic on the backend side, allowing the business logic to be reused by other technologies (or even future technologies) Currently the abap reports do not offer any reuse. The usage of the business logic is limited to the R3 reports; the communication between the business logic within reports to external services is not possible. Therefore, when developing new functionality, try to separate the object models from the business logic models and from the user interface, so that they can be reused when appropriate. For further information on separation of concerns, you can study the concepts of MVC pattern.

Guideline from SAP standard guidelines document regarding separation of concerns: Priority Strongly Recommended Rule You should always follow the concept of separation of concerns: Model your applications strictly service-oriented: always disentangle the logics for presentation, application, and database access. A software layer should never produce data that it consumes. Especially no database accesses are allowed in the layers for presentation or application and vice versa, the persistency and the application layer are

For SAP internal use


P a g e | 12 not allowed to carry out an own user dialog. Instead, reusable services that are appropriate and reliable must be called. Rationale Separation of concerns is the basic programming model for supporting stability and maintainability because it allows programming of reusable services and testing of business logic by using isolated unit tests. Known Problems None Exceptions None

8 GLOBAL objects VS local objects


One of the most common impediments of creating objects for each single business rule is that, as a common belief, each single object must be created as a global class in SE24. This would result into an enormous quantity of objects for each subject and localization. In addition, the objects would all be scattered into the same package (PB__ and PC__) making it very difficult to identify groups of objects that belong to the same functionality or dependency between them (since all objects are global they all can communicate with each other differently than on Java). The following sections explains how to structure the objects between local and global definitions as a way to balance visibility and reusability between functionalities

9 Local classes in reports


New reports must contain no business logic directly implemented on its body (via forms or functions, or business logic on its screens). It is a good practice to define local objects within the report to control screen events and output results. For example, calling a method in each report event and this method will delegate actions to sub objects. As a rule, common reusable reporting functionalities should be encapsulate in global objects and reused in different reports. For example, it is possible that certain localization has a group of parameters, search helps and buttons with the same consistency and selection logic in different reports (today, via copy and paste code), this can be reused if implemented as objects. It is a bad practice to locally define functionalities in a report, in case these functionalities could be reused in similar reports. Specific reporting logic, which cannot be reusable in other functionalities, may be defined as local objects in the report itself. No new Forms.

For SAP internal use


P a g e | 13 In case you dont see the immediate necessity to define the object as global, you may define it locally within a report, and when necessary, migrate it to a global object (SE24 has an migration assistant for local objects in includes)

Migrating local objects to global with SE24

10 Class pools in global classes


Global classes are the classes defined in transaction SE24; they are globally visible within the system similar to a DDIC object (data elements, structures, etc...). There is no technical package dependency between objects like in Java, therefore it is important to check if you are authorized to use the objects from that specific package (different packages present on a SAP development system may not be installed on customer systems). It is a good practice to define business logic as a global class and break down the functionality into several smaller objects defining subclasses into the body of the global class. The methods and attributes (at least the public ones) of a global class are exploitable by other objects or reports, however, the local objects - defined into the global class are not. The global class should expose a limited and reasonable number of methods to the external world and be internally modularized with a reasonable number of objects. It is a good practice to make usage of polymorphism to modularize the local objects of the global class allowing them to share common properties of functionalities. It must be taken into account that important objects may not be able to be defined as local objects, since they are not easily reusable in other global classes or reports. There is only one way to reuse the implementation of local objects outside the global class; this is somehow useful (see explanation in chapter below). Example of global class internally modularized with subclasses:

For SAP internal use


P a g e | 14

One thing to take care is that local classes of a global class cannot be statically accessed outside even in inheriting classes or on global friends classes. As a conclusion, you can use local classes in a global class to apply internal modularization thus avoiding two common bad practices: Global classes with many methods (public and private) Methods with large number of code lines (a method should not exceed 50 lines of code, as it becomes unreadable and avoids internal modularization / code reuse; see SAP development guidelines) Two additional guidelines linked to this subject extracted from the state-of-the-art abap programming:

10.1 Design classes with a reasonable size

You should design classes (and function groups where needed) with reasonable size (that is number of methods or functions). Rationale Classes (or function groups) should be dedicated to a well defined purpose, so that no large class (function group) has to be loaded while only one single component (function module) of it is used.

10.2 Use Local Classes to Modularize Class Pools

For purposes of internal modularization you may use local classes in class pools. Rationale This is preferable to private methods of the global class because it keeps your main classes slim and their interfaces readable.

For SAP internal use


P a g e | 15

11 Designing classes with correct data encapsulation


It is a common bad practice to have global classes containing mostly (or only!) public attributes. The purpose of class attributes is to serve only as the instances internal data container (when not static). If the internal data is commonly used outside the class, then there is a design conceptual error in the class. In other words, the class attributes must be handled within the class, not outside the class. A good example to illustrate this idea is the telephone object: you dont care how the data and voice is stored or transmitted inside the telephones device; you just care if you can hear well and if the other person can hear you well. The problem of handling class data outside the class is simply the fact that this is considered to be a procedural programming model. It is not an object oriented model. This will lead to the basic issues of procedural programming: changing global data in many different places; copy and paste of similar procedures and side effects on data handling. It is a good practice to encapsulate data exploiting the usage of different instances of an object instead of using one object instance and replace the data inside it several times (when a new object should be created). Therefore use mainly private or protected attributes. Remember that private attributes are visible and allowed to be modified between different instances of the same object. Within the class, the attributes must be modified in the minimum possible number of methods. This is a good practice following the code modularization principle and avoids side effects on global data. Prefer to use setters whenever the attribute must follow a specific condition or if it is to be modified from outside the class (not recommended); The correct data encapsulation and minimum attribute (global data) manipulation is a very important concept for robust OO development. Some entities that you currently handle as internal tables can be handled as objects. Usually in this case the internal table is composed only by its key and the object reference is stored on a table field. This allows Loops with where clause and allows the table rows (objects) to perform actions with themselves.

12 Memory usage
Currently it is not technically possible to define a class destructor in abap. The memory occupied by object instances is released by the garbage collector when there are no more references pointing at the memory. The abap command CLEAR can be used to un-assign the instances reference from the reference variable. In case you work with several instances containing heavy data, you may implement a destructor method that clears all attributes data (variables and internal tables). This is useful when you know exactly that you will not need an instance anymore (call the destructor method and then clear the reference variable).

For SAP internal use


P a g e | 16 In this way, the memory consumption is reduced immediately and avoid waiting for the garbage collector to do most of the job.

12.1 Whenever possible, use Lazy Instantiation


The concept of Lazy Instantiation is that memory should only be allocated when necessary. In ABAP, this type of concept requires a different approach, since most of the time we have tables and structures contained in classes. For handling database table entries, decreasing memory usage and increasing processing speed, a buffer of only required entries should be implemented. The process should be the following:

This way, only the data that has been used at least once is stored in the attribute, and further access to the Database isnt required for the same entry. Of course this solution is only feasible in the cases that at least the Bufferized Key is specified completely. Two guidelines extracted from SAP standard guidelines document:

For SAP internal use


P a g e | 17

Priority Recommendation Rule You should restrict the modification of data that are global for a method (attributes of the own class, public attributes of other classes, global data of the ABAP program) to a minimum inside the method. If you modify data that are global for a method inside a method, avoid the modification of such global data in too many methods and. use only dedicated methods that can be recognized, e.g., via their naming (set_ methods). Rationale A method should be free of side effects. Known Problems Exceptions None Priority Hint Rule You might use the appropriate selectors for accessing attributes of the same class inside the method. Rationale Distinguish attributes from the methods local data, if naming conventions are not followed or not sufficient. Known Problems Readability of code Exceptions Naming conventions are sufficient for the distinction. Bad Example METHOD meth. DATA lv_var TYPE ... mv_attr1 = gv_attr2 + lv_var. ENDMETHOD. Good Example METHOD meth. DATA lv_var TYPE ... me->mv_attr1 = class=>gv_attr2 + lv_var. ENDMETHOD.

12.2 Do not modify global data in methods

12.3 Access Attributes in Methods via Selectors

For SAP internal use


P a g e | 18

13 Inheritance and polymorphism


Inheritance and polymorphism are perhaps the main advantage of OOP in respect to classic procedural programming. These aspects are commonly known and used in all software development industry. It is also a key concept for code and functionality reuse (see example below). This concept is fully supported in ABAP OO and there is nothing special to be taken care of with respect to other programming languages (except that ABAP does not support multiple inheritance). Developers should be encouraged and motivated to apply the OO design patterns and use it appropriately. There are some programming principles that can be considered in order to assist on object modeling such as: Case-less programming and DRY (dont repeat yourself; this principle has been formulated by Andy Hunt and Dave Thomas in their book The Pragmatic Programmer; a recommended reading). Example: Bellow, an usual example of processing internal tables chained with a CASE switch and calling a specific FORM (compensation_(x)) for each specific case. The general reusable FORM routine (balance_debit_credit) also has a specific logic for only one case: Procedural style: .
LOOP AT mt_rules INTO ls_rule. DO. LOOP AT lt_credits INTO ls_credits WHERE tax_code = ls_rule-tax_code1. LOOP AT lt_debits INTO ls_debits WHERE tax_code = ls_rule-tax_code2. CASE ls_rules-section. WHEN 'A'. PERFORM compensation_a CHANGING ls_credits ls_debits. PERFORM balance_debit_credit CHANGING ls_credits ls_debits.

WHEN 'B'. " similar to 'a' above, just a copy and past with a few differences PERFORM compensation_b CHANGING ls_credits ls_debits. PERFORM balance_debit_credit CHANGING ls_credits ls_debits.

WHEN 'C' OR 'D' OR 'E'. "similar to the ones above, just a copy and past with a few differences PERFORM compensation_c CHANGING ls_credits ls_debits. PERFORM balance_debit_credit CHANGING ls_credits ls_debits.

WHEN OTHERS. "... ENDCASE. PERFORM append_result USING ls_credits ls_debits. ENDLOOP. ENDLOOP. ENDDO. ENDLOOP.

Notice below, the special case for section = E. this case is only necessary for one section, but is passed by for each other section cases. This is a common situation on ERP applications implementing procedural style.

For SAP internal use


P a g e | 19
*&---------------------------------------------------------------------* *& Form balance_debit_credit *&---------------------------------------------------------------------* FORM balance_debit_credit CHANGING p_credit p_debit. IF p_credit-amount > p_debit-amount. IF p_credit-section = 'E'. "something specific for section E RETURN. ENDIF. "do something ELSE. "do something else ENDIF. ENDFORM. "balance_debit_credit

The same logic could be written in a cleaner and more reusable style by applying the OO polymorphism:
LOOP AT mt_rules ASSIGNING <ls_rules>. DO. lr_credit = mr_credits->read_entry( iv_sectn = <ls_rules>-sectn1 iv_tax_code = <ls_rules>-tax_code1 ). IF lr_credit IS INITIAL. EXIT. ELSE. lr_debit = mr_debits->read_entry( iv_sectn = <ls_rules>-sectn2 iv_tax_code = <ls_rules>-tax_code2 ).

ls_result = lr_credit->compensate( lr_debit ). Log(ls_result ) ENDDO. ENDLOOP.

In this case, each object (debit or credit of type A, B, C, D or E) is a child of object of a more general tax code, inheriting the general properties and overwriting each specific need. A correction or change in the implementation of one of these child objects will not cause a side effect on the others; at the same time, it is easy to create a side effect on different tax codes by modifying the
FORM balance_debit_credit

The object oriented programming paradigm is a vast theory and developers may require time to become a real user of all its techniques and advantages. For example, the following link lists a variety of design patterns for objects: http://en.wikipedia.org/wiki/Design_pattern_(computer_science) http://en.wikipedia.org/wiki/Object-oriented_programming

For SAP internal use


P a g e | 20

14 Static methods vs. Singleton

Another very common practice nowadays is the creation of service methods as static methods in global classes. For example, it is common to see static methods for reading data from database tables. This appears useful since the method can be called from anywhere and also allows internal buffering (defining static data within methods) avoiding redundant access to database or recalculations. A much better approach than standalone static methods is to use singleton objects. Singletons are common also in other programming languages. In abap, a singleton object is characterized for having a private constructor and a static public method that returns an instance of the object; the first time this method is called, it stores the instance in a static attribute. This static method is commonly known as factory method. Example of singleton factory method (this case is actually considered a multiton since more than one instance can be created according to the taxcode type):
METHOD get_instance. FIELD-SYMBOLS <ls_instance> TYPE lty_s_instance. DATA ls_instance TYPE lty_s_instance. READ TABLE mt_instances ASSIGNING <ls_instance> WITH KEY taxcode = iv_taxcode. IF sy-subrc <> 0. CASE iv_taxcode(1). WHEN 'A'. CREATE OBJECT lr_tax_code_impl TYPE lcl_taxcode_a. WHEN 'B'. CREATE OBJECT lr_tax_code_impl TYPE lcl_taxcode_b. WHEN OTHERS. * RAISE exception cx_invalid_taxcode. ENDCASE. ls_instance-taxcode = iv_taxcode. CREATE OBJECT ls_instance-instance EXPORTING p1, p2, p3... APPEND ls_instance TO mt_instances ASSIGNING <ls_instance>. ENDIF. rr_instance = <ls_instance>-instance. ENDMETHOD.

There are many benefits on using the singleton approach; the main one is the control of instantiation: it is possible to instantiate different instances based on the key properties of the object and keep track of all instances by using a static internal table declared locally in the static method (factory method). In this way, when the factory method is called from a program or object which already instantiated the singleton, it just returns the reference of the existing instance instead of creating a new instance. Another benefit of singleton comparing to standalone static methods is that the service method has access to all of the other (instance) methods in the class (since it is no more static). This is another point that helps the object to be better modeled within abap workbench. A class with many static methods will probably share static attributes between its methods, leading to a similar global variables approach, which will lead to procedural programming style within the methods. (Extracted from the state-of-the-art ABAP programming article):

14.1 Related guidelines

For SAP internal use


P a g e | 21 Consider using private instantiation for your classes (CREATE PRIVATE) and offer appropriate factory methods5. This allows you to: o o o o Control the instantiation of your class from within the class Control the parameters passed to the constructor Handle the instances of your class within the class Share the instances of your class effectively by passing the same object reference to different clients when appropriate.

o Release clients from activating and deactivating event handlers Other recommendations for the practical use of ABAP Objects that give your programs an adequate structure: For purposes of internal modularization, exploit local classes in class pools. This is preferable to private methods of the global class. With that, you can keep your main classes slim and their interfaces readable. For internal modularization of a class pool, prefer instance methods in local singletons6 versus static methods of local classes. Two important reasons to prefer instantiation over the direct use of classes via static methods are o o That you can use inheritance and method redefinitions7, if needed, and That you can control the moment of the instantiation and the lifetime of the object.

A factory method is a method normally static of a class that creates an object of that class and returns a reference that points to that object. 6 A singleton is an object-oriented pattern. A class defined according to the singleton pattern is implemented in such a way that exactly one object can be instantiated from that class. A possible way to do so is to define a class with private instantiation and to use the CREATE OBJECT statement in the static constructor only. 7 Remember, that you cannot redefine static methods in ABAP Objects.

For SAP internal use


P a g e | 22

15 Interface composition
Interfaces are a very useful tool in order to achieve polymorphism. Within ABAP, the interfaces work very similar to classes: they are globally defined in transaction SE24 or locally within global classes or reports. It is not possible to create an interface pool inside the local types declaration of a global interface.

Remember you can inherit more than one interface in the same object and work with these interfaces independently. For example, the object lcl_example inherits the interfaces if1 and if2, the same instance is handled by two different interfaces within the program:
DATA lr_example TYPE REF TO lcl_example. DATA lr_if1 TYPE REF TO lif1. DATA lr_if2 TYPE REF TO lif2. CREATE OBJECT lr_example. lr_if1 = lr_example. lr_if1->test1( ). lr_if2 = lr_example. lr_if2->test2( ).

For SAP internal use


P a g e | 23

15.1 Additional guidelines on interfaces


When you are addressing objects of classes that implement standalone interfaces, you should generally do so by using interface reference variables for accessing the interface components (using iref>comp). If the class provides an alias for an interface component, this component counts as a class component and you might address it via a class reference variable and the alias name. But please note that the interface component selector (~) should never be used by the outside user to address an interface component of an object via a class reference variable (dont use cref->intf~comp). The reason is that the outside users view of a class should be defined by the public interface offered by the class. The public interface of a class consists of its own class components and can be enhanced by interfaces. The enhancement of the public interface of a class with interface components does not introduce a new hierarchy level. All components of the class lie on the same level but are offered from different parts of the public interface. This is expressed, by using the two-level-expression iref>comp instead of the three-level cref->intf~comp, where iref and cref denote which part of the public interface is addressed. Therefore, iref->comp is more than just a shortcut for cref>intf~comp. Accessing a class via an interface reference variable always expresses that you are interested in the aspect of the class that is offered by the interface. (See sidebar: Accessing Interface Components.) This example shows how the interface components of objects (i.e., the components of objects that are components of some interface implemented by the objects class) can be accessed and which access is recommended. The interface method imeth of the object referenced by cref1 should be accessed via an interface reference variable only. In the object referenced by cref2, it can be accessed alternatively via its alias name meth. In all cases, the usage of the interface component selector ~ is not recommended, because it introduces a hierarchical view on components that that are part of the same level.
INTERFACE intf. METHODS imeth. ENDINTERFACE. CLASS c1 DEFINITION. PUBLIC SECTION. INTERFACES intf. ENDCLASS. CLASS c1 IMPLEMENTATION. METHOD intf~imeth. ... ENDMETHOD. ENDCLASS. CLASS c2 DEFINITION. PUBLIC SECTION. INTERFACES intf. ALIASES meth FOR intf~imeth. ENDCLASS. CLASS c2 IMPLEMENTATION. METHOD intf~imeth. ... ENDMETHOD. ENDCLASS. DATA: iref TYPE REF TO intf,

For SAP internal use


P a g e | 24
cref1 TYPE REF TO c1, cref2 TYPE REF TO c2. ... CREATE OBJECT: cref1, cref2. iref = cref1. iref->imeth( ). cref1->intf~imeth( ). iref = cref2. iref->imeth( ). cref2->meth( ). cref2->intf~imeth( ). "recommended "recommended "not recommended "recommended "not recommended

15.1.1 Use Inheritance Moderately Priority Recommended Rule You should use inheritance moderately. Especially you should avoid deep inheritance trees. In case of doubt, prefer interfaces to achieve polymorphism. For the reuse of interfaces, exploit the concept of interface composition in favor to inheritance. Rationale It is almost impossible to change (maintain) other than private components of superclass with many subclasses. This is especially true for classes in frameworks having subclass in different systems. Known Problems None Exceptions Application is thoroughly modeled following accepted and robust OO-design rules.

16 Data Types within classes and method parameters


In most occasions, it is necessary to handle internal tables within classes. The abap-objects enforces the usage of table types (local or global) on methods signatures and on attributes. It is not recommended to create a DDIC table type for each single internal table that you need to manage in the class. The following guidelines can be followed: For private/protected attributes and method parameter types (valid for all releases): o If the internal table is not to be public exposed you should define its table type as an local type (see picture below) :

For SAP internal use


P a g e | 25

For public attributes and method parameter types (valid as of release ECC 500) o Define the types in the public section as seen in the picture below:

For public attributes and method parameter types (valid for releases 46c and 470) o Define the types in a type-pool (even if they are declared as obsolete, it is ok to do it in old/obsolete releases):

For SAP internal use


P a g e | 26

Take care that on release 46c, the changes on type pools are not supported by correction instructions!

Sometimes you will need to refer to classes definitions before they are declared (inside the local definition of global class for example); Read the abap keyword documentation for the both commands below Definition deferred Definition load

Priority Mandatory Rule If you create new function modules or add parameters to existing function modules you must not use the parameter kind TABLES. Rationale TABLES is an obsolete parameter kind for internal tables with header lines. Known Problems None Exceptions You add parameters to existing function modules that already contain TABLES parameters. Bad Example FUNCTION func. *"-------------------------------------------------------------*" TABLES

16.1 Do not use obsolete Parameter Kinds

For SAP internal use


P a g e | 27 *" para TYPE struc

Good Example FUNCTION func. *"-------------------------------------------------------------*" CHANGING *" para TYPE table_type

Priority Mandatory Rule If you create new function modules or add parameters to existing function modules you must not type parameters with the additions LIKE or STRUCTURE. You must use TYPE instead. Rationale LIKE and STRUCTURE are obsolete additions for typing parameters, that support implicit casting of actual parameters Known Problems None Exceptions None Bad Example FUNCTION func. *"-------------------------------------------------------------*" IMPORTING *" para1 LIKE struc-comp para2 STRUCTURE struc. Good Example FUNCTION func. *"-------------------------------------------------------------*" IMPORTING *" para1 TYPE struc-comp para2 TYPE struc.

16.2 Do not use obsolete Parameter Typings

16.3 Type Parameters as Specific as Possible


Priority Strongly recommended

Rule You should always type formal parameters as specific as possible. Avoid to use the most generic types as any or REF TO object. If you do not want to offer generic services, you should always type completely. For GET methods, for performance reasons array interfaces can be appropriate to read more than just one attribute. Rationale

For SAP internal use


P a g e | 28 The more generally a parameter is typed, the more careful you must be when using it in order to avoid (runtime errors). Only complete typing of formal parameters guarantees that your code always behaves in the same way and that it can be tested locally. Known Problems None Exceptions None Bad Example CLASS cl_class DEFINITION ... PUBLIC SECTION. METHODS meth IMPORTING iv_p TYPE any. ENDCLASS. CLASS cl_class IMPLEMENTATION. METHOD meth. FIND REGEX ... IN iv_p ... ENDMETHOD. ENDCLASS. Good Example CLASS cl_class DEFINITION ... PUBLIC SECTION. METHODS meth IMPORTING iv_p TYPE csequence.

17 Local types and implementation includes of global classes


On releases 620 to 701 (SAP HR 470 604), there are two separate includes available for defining local classes (or types) and for implementing local classes (on release 46c both definition and implementation must be done in the include local types). This separation was actually a recommendation; SAP has reviewed the recommendation, stating that both includes serve both for definition and implementation of local classes; In fact the screen of transaction SE24 has been changed on basis release 7.1 in order to clarify the difference between these two includes:

Release 701: Buttons to jump to local types and implementation includes

For SAP internal use


P a g e | 29

Release 7.1: the former button implementations is now renamed in order to comply with the new recommendation (see for example system EH5).

Release 7.1: The include local types can be found at the Goto menu and has also been renamed. The following information is displayed when these includes are accessed for the first time in transaction SE24 in new releases:

New comments inside include Local Definitions/Implementations: *"* use this source file for the definition and implementation of *"* local helper classes, interface definitions and type *"* declarations

For SAP internal use


P a g e | 30

New comment on include Class-Relevant Local Definitions: *"* use this source file for any type of declarations (class *"* definitions, interfaces or type declarations) you need for *"* components in the private section There are a few technical reasons for why is it better to define local classes in the former implementation include: Classes defined in this include can inherit and have access to the global class attributes and methods; this allows to explore additional design patterns (see next chapter) Changes on this include do not trigger a full recompilation of the global class The include of local types can be used, from now on, mainly for private data types declaration and definition/implementation of classes relevant only for the private section of the global class. Nothing has been changed from the compiling point-of-view between the releases (since 470), it regards only a better recommendation on how to use these includes (the code you developed in a specific include of the global class will always be supported in higher releases).

18 Global class with multiple implementations


The usage of polymorphism can be simplified in terms of number of global objects by implementing local classes (in a global class) which inherit from the global class itself. This avoids defining one global (reusable) object per each redefinition. For example: tax codes, containing an amount, may behave differently depending on the tax codes type. In the example below the global class zcl_taxcode is instantiated as tax code A1 and later as B1. Both receive an amount of 6000,00 which is added to a private amount attribute. However, the implementation for tax code A1 is designed to limit the amount to 5000,00 (the attribute may never exceed 5000,00) After the execution of the statements below, the amount attribute of tax code A1 will result in 5000,00 and B1 will result in 6000,00

For SAP internal use


P a g e | 31
REPORT z_test.

DATA lr_taxcode TYPE REF TO zcl_taxcode. START-OF-SELECTION. lr_taxcode = zcl_taxcode =>get_instance( iv_taxcode = 'A1' ). lr_taxcode->add_amount( iv_amount = 6000 ). lr_taxcode = zcl_taxcode =>get_instance( iv_taxcode = 'B1' ). lr_taxcode->add_amount( iv_amount = 6000 ).

See below the technical properties and source code for this example: The global class must be defined as Abstract (abstract constructor) The global class has a (public) constructor for initializing private attributes; There is no problem to define the constructor as public since the class cannot be instantiated from outside (abstract) The global class has a public factory method defined as static that controls the instantiation of the redefined local classes and returns the relevant instance. This method can also serve for singleton/multiton implementation in the case you want to create only one instance of a tax code and return that instance if already created (the instance(s) must be stored in a private static attribute/internal table) The global class has a public method add_amount which adds an amount parameter to the taxcode amount. The real implementations of tax codes inherit from the abstract tax code. Class (and local classes) source code: class ZCL_TAXCODE definition public abstract create public . public section. methods CONSTRUCTOR importing !IV_LGART type LGART . methods ADD_AMOUNT importing !IV_AMOUNT type F . methods GET_AMOUNT returning value(RV_AMOUNT) type MAXBT . class-methods GET_INSTANCE importing !IV_LGART type LGART returning

For SAP internal use


P a g e | 32 value(RR_INSTANCE) type ref to ZCL_TAXCODE . protected section. data MV_AMOUNT type MAXBT . data MV_LGART type LGART . private section. endclass. "ZCL_TAXCODE definition *"* local class implementation METHOD ADD_AMOUNT. mv_amount = mv_amount + iv_amount. ENDMETHOD. METHOD get_amount. rv_amount = mv_amount. ENDMETHOD. METHOD get_instance. CASE iv_lgart(1). WHEN 'A'. CREATE OBJECT rr_instance TYPE lcl_taxcode_a EXPORTING iv_lgart = iv_lgart. WHEN 'B'. CREATE OBJECT rr_instance TYPE lcl_taxcode_b EXPORTING iv_lgart = iv_lgart. WHEN OTHERS. * RAISE exception cx_invalid_taxcode. ENDCASE. ENDMETHOD. method CONSTRUCTOR. mv_lgart = iv_lgart. endmethod. endclass. "ZCL_TAXCODE implementation *----------------------------------------------------------------------* * CLASS lcl_taxcode_a DEFINITION *----------------------------------------------------------------------*

For SAP internal use


P a g e | 33 * *----------------------------------------------------------------------* CLASS lcl_taxcode_a DEFINITION INHERITING FROM zcl_taxcode. PUBLIC SECTION. METHODS add_amount REDEFINITION. ENDCLASS. "lcl_taxcode_a DEFINITION **----------------------------------------------------------------------* ** CLASS lcl_taxcode_b DEFINITION **----------------------------------------------------------------------* ** **----------------------------------------------------------------------* CLASS lcl_taxcode_b DEFINITION INHERITING FROM zcl_taxcode. PUBLIC SECTION. METHODS add_amount REDEFINITION. ENDCLASS. "lcl_taxcode_b DEFINITION *----------------------------------------------------------------------* * CLASS lcl_taxcode_a IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_taxcode_a IMPLEMENTATION. METHOD add_amount. super->add_amount( EXPORTING iv_amount = iv_amount ). IF mv_amount > 5000. mv_amount = 5000. ENDIF. ENDMETHOD. "add_amount ENDCLASS. "lcl_taxcode_a IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_taxcode_b IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_taxcode_b IMPLEMENTATION. METHOD add_amount. super->add_amount( EXPORTING iv_amount = iv_amount ). IF mv_amount < 0. mv_amount = 0. ENDIF. ENDMETHOD. "add_amount ENDCLASS. "lcl_taxcode_b IMPLEMENTATION

For SAP internal use


P a g e | 34

19 Using events

Events are primarily essential for user interface programming, for example, to execute some action when a button is clicked or when a screen is to be showed. Events are very important in UI programming because, for example, you cannot predict when the user will click on a certain button; therefore events are very useful to trigger actions that dont follow a specific sequence.

Example of event schema In HCM or ERP applications, the situation within reports is different. The actions and calculations must be executed in a very specific sequence. Therefore at first sight, events may not look useful for business logic programming. There are however a few useful situations where events can benefit also on BL programming, such as: Logging Collecting information from objects, like amounts, properties, even if they are in an internal table without having to loop at them Centrally handle actions performed by objects Controlling objects without having to loop at each one. The Case study chapter exemplifies the usage of events

For SAP internal use


P a g e | 35

20 Friend classes
Friend classes are allowed to access and modify the private attribute or call private methods of the other class (defined as Friend). This practice is not recommended in general since it may easily lead to a procedural programming model, where the attributes of classes are global data and methods represent the classic FORMS. The text below was extracted from the BC401 handbook: In some cases, classes have to work together so closely that one or both classes need access to the others protected and private components. Similarly, they need to be able to create instances of these other classes regardless of the visibility of the constructors. To avoid making these options available to all users, you can use the concept of class friendship. A class can provide friendship to other classes and interfaces (and hence all classes that implement the interface). This is the purpose of the FRIENDS addition of the CLASS statement and the Friends tab in the Class Builder. There, all classes and interfaces that grant friendship are listed. In principle, granting friendship is one-sided: A class granting friendship is not automatically a friend of its friends. If a class granting friendship wants to access the non-public components of a friend, this friend must also explicitly grant friendship to it.

Figure 160: Definition of Friendship Relationships The friend property is inherited: Classes that inherit from friends and interfaces containing a friend (as a component interface) also become friends. Therefore, extreme caution is advised when granting

For SAP internal use


P a g e | 36 friendship. The higher up a friend is in the inheritance tree, the more subclasses can access all components of a class that grants friendship. Conversely, granting friendship is not inherited. A friend of a superclass is therefore not automatically a friend of its subclasses.

Figure 161: Areas of Use for Friendship Relationships A typical application of the friend relationship between classes is when methods that access the same data are distributed over several classes. This common data is, however, to be protected from access by foreign classes. In such friendships, you can make the class containing the data a singleton - that is, make sure it can only be instantiated once in each program instance.

For SAP internal use


P a g e | 37

20.1 How to reuse local objects outside the global class


In case you need a general object with many redefined objects (for example: tax groups with similar calculation) you may redefine this object as local classes and use these local classes instances outside the global class as long as the general object is defined as global (the local classes must inherit a global interface(s) of global class). This is possible but not necessarily the best practice among object modeling. This practice can be useful mainly on cases in which there is a restriction on creating a large number of global classes.

For SAP internal use


P a g e | 38

21 Case study
The case study demonstrates the usage of object oriented programming for a payroll function. The business logic in this example does not represent any real logic, but only illustrative calculations that could be confronted with real cases. The objectives in this case study are: Model business logic as object oriented Reuse of functionality Separation of concerns regarding application log (user interface) Technical targets of this case study: Modeling objects and processes Good usage of interfaces Inheritance Polymorphism Exploiting Events Data encapsulation

21.1 Case study requirements:


Business logic requirements: It is necessary to calculate a few specific taxes over certain income wagetypes8 when these are transferred to table RT 9. There are two kinds of taxes: o The usual tax is calculated based on 20% of the total taxable income o The state tax is calculated also based on 20% of the total taxable income, however this tax is limited to 100,00. The taxable income is identified by all wagetypes that are named starting with characters /1. Application specific requirements: The application should present a Trace log showing all calculations performed step by step By some technical reason, the payroll consultants want the state tax to be blocked for modifications in further processing whenever its amount reached the legal limit of 100,00.

21.2 Solution
The implementation of this case study is composed by the following objects: Wagetype: each wagetype represent an employee specific amount, like income, tax, deductions, etc..; the wagetype is basically composed by its name and an amount.
8 9

Employees related salaries, incomes, deductions, etc... During payroll calculation, table RT contains final amounts, which are no more modified during payroll execution.

For SAP internal use


P a g e | 39

Tax wagetype: inherits from the general wagetype, but has an specific business logic to determine its tax amount based on 20% of income State-tax wagetype: inherits from the tax wagetype, having an additional logic to limit its tax amount to 100,00 and lock its modification after reaching the 100,00 limit. Table RT: an object that represents a collection of wagetypes Log handler: an object to handle the application log Interface loggable: any objects that inherit this interface become loggable by the log handler. In this case, all wagetypes and the RT table will log on the application. Payroll function: an object to control the calculation flow; For keeping the example simple, it will be a local class within a report.

21.3 Application log implementation:


The loggable interface offers an event in which objects can notify when a certain action occurred; this event imports a few parameters like the message text, object name, message type. The wagetype objects (and also RT table) raise events to notify the log when the certain actions occurred, like when a wagetype was created, or when an amount was added to the wagetype. The log handler calls the appropriate UI service (in this case it is simply a write in the screen).

21.4 Tax on wagetypes


The tax on tax wagetypes are determined by a handler that listens to when a taxable income is added to RT.

21.5 Workflow:

The following tax wagetypes are instantiated: Tax /600 with amount = 0 State tax with amount /650 = 0. The wagetypes income wagetypes are instantiated: /101: with amount 1000,00 /102: with amount 2000,00 /103: with amount 3000,00 Later, the amount 1000,00 is added to /103 , and then these income wagetypes are added to RT. Finally the tax wagetypes are added to RT and the processing is finished.

For SAP internal use


P a g e | 40

This is the log written by the function for the workflow above: /600 /650 /101 /600 /650 /650 /101 /102 /600 /650 /102 /103 /103 /600 /650 /103 /650 /600 /650 /650 I I I I I I I I I I I I I I I I I I I I wt created - 0.00 wt created - 0.00 wt created - 1000.00 amount added - 200.00 amount added - 200.00 add to tax - amount limited to 100 and locked wt added to RT - /101 1000.00 wt created - 2000.00 amount added - 400.00 add to tax - wagetype is locked and cannot be modified wt added to RT - /102 2000.00 wt created - 3000.00 amount added - 1000.00 amount added - 800.00 add to tax - wagetype is locked and cannot be modified wt added to RT - /103 4000.00 add to tax - wagetype is locked and cannot be modified wt added to RT - /600 1400.00 add to tax - wagetype is locked and cannot be modified wt added to RT - /650 100.00

21.6 Result of workflow

21.6.1 Class diagram

For SAP internal use


P a g e | 41

21.6.2 Source code of classes All source code can be accessed in development system L7D, program zgh_teste2. The source code below was printed from the SE24 transaction; there are some uppercase/lowercase formatting differences. 21.6.2.1 Interface ZIF_LOGGABLE
interface ZIF_LOGGABLE public . events LOGPOINT_ADDED exporting value(IV_MESSAGE_TEXT) type CLIKE optional value(IV_MSG_TYPE) type MSGTY optional value(IV_EVENT_NAME) type CLIKE optional value(IV_OBJECT_NAME) type CLIKE optional . endinterface.

For SAP internal use


P a g e | 42 21.6.2.2 Wagetype object
class-pool . class ZCL_WT definition public create public . public section. interfaces ZIF_LOGGABLE . data MV_LGART type LGART read-only . methods ADD_AMOUNT importing !IV_AMOUNT type MAXBT . methods CLEAR_AMOUNT . methods ADD_WT importing !IR_WT type ref to ZCL_WT . methods CONSTRUCTOR importing !IV_LGART type LGART !IV_AMOUNT type MAXBT . methods GET_AMOUNT returning value(RV_AMOUNT) type MAXBT . protected section. aliases LOGPOINT_ADDED for ZIF_LOGGABLE~LOGPOINT_ADDED . data MV_AMOUNT type MAXBT . private section. events AMOUNT_ADDED exporting value(IV_AMOUNT) type MAXBT . events WT_CREATED exporting value(IR_WT) type ref to ZCL_WT . events WT_ADDED exporting value(IR_WT) type ref to ZCL_WT . endclass. "ZCL_WT definition

<-the attribute is public but set as read-only, this is an ABAP feature to avoid getter in case the attribute is widely accessed outside. This should be used only for key attributes

For SAP internal use


P a g e | 43
*"* local class implementation METHOD add_amount. DATA lv_text TYPE string. mv_amount = mv_amount + iv_amount. lv_text = iv_amount. RAISE EVENT logpoint_added EXPORTING iv_message_text = lv_text iv_msg_type = 'I' iv_event_name = 'amount added' iv_object_name = mv_lgart. RAISE EVENT amount_added EXPORTING iv_amount = iv_amount. ENDMETHOD. method ADD_WT. add_amount( ir_wt->mv_amount ). endmethod. METHOD constructor. DATA lv_text TYPE string. mv_lgart = iv_lgart. mv_amount = iv_amount. lv_text = mv_amount. RAISE EVENT logpoint_added EXPORTING iv_message_text = lv_text iv_msg_type = 'I' iv_event_name = 'wt created' iv_object_name = mv_lgart. RAISE EVENT wt_created EXPORTING ir_wt = me. ENDMETHOD. method GET_AMOUNT. rv_amount = mv_amount. endmethod. endclass. "ZCL_ WT implementation

21.6.2.3 RT class
class-pool . TYPES: BEGIN OF lty_s_rt, lgart TYPE lgart, o_wt TYPE REF TO zcl_wt, END OF lty_s_rt. TYPES lty_t_rt TYPE TABLE OF lty_s_rt. class ZCL_RT definition public

For SAP internal use


P a g e | 44
create public . public section. interfaces ZIF_LOGGABLE . events WT_INSERTED exporting value(IR_WT) type ref to ZCL_WT . methods INSERT_WT importing !IR_WT type ref to ZCL_WT . methods GET_WT importing !IV_LGART type LGART returning value(RR_WT) type ref to ZCL_WT . private section. aliases LOGPOINT_ADDED for ZIF_LOGGABLE~LOGPOINT_ADDED . data MT_WTS type LTY_T_RT . events WT_ACCESSED exporting value(IR_WT) type ref to ZCL_WT . endclass. "ZCL_RT definition *"* local class implementation METHOD insert_wt. DATA ls_wt LIKE LINE OF mt_wts. DATA lv_amount TYPE maxbt. DATA lv_text TYPE string. FIELD-SYMBOLS <ls_wt> LIKE LINE OF mt_wts. lv_amount = ir_wt->get_amount( ). READ TABLE mt_wts ASSIGNING <ls_wt> WITH TABLE KEY lgart = ir_wt->mv_lgart. IF sy-subrc = 0. <ls_wt>-o_wt->add_wt( ir_wt ). ELSE. ls_wt-lgart = ir_wt->mv_lgart. ls_wt-o_wt = ir_wt. APPEND ls_wt TO mt_wts. ENDIF. RAISE EVENT wt_inserted EXPORTING ir_wt = ir_wt. lv_text = lv_amount. CONCATENATE ir_wt->mv_lgart lv_text INTO lv_text SEPARATED BY space. RAISE EVENT logpoint_added EXPORTING iv_message_text = lv_text iv_msg_type = 'I' iv_event_name = 'WT added to RT' iv_object_name = ir_wt->mv_lgart.

For SAP internal use


P a g e | 45
ENDMETHOD. method GET_WT. FIELD-SYMBOLS <ls_wt> LIKE LINE OF mt_wts. DATA ls_wt LIKE LINE OF mt_wts. READ TABLE mt_wts ASSIGNING <ls_wt> WITH TABLE KEY lgart = iv_lgart. IF sy-subrc = 0. rr_wt = <ls_wt>-o_wt. RAISE EVENT WT_accessed EXPORTING ir_wt = <ls_wt>-o_wt. ELSE. "raise... ENDIF. endmethod. endclass. "ZCL_RT implementation

21.6.2.4 log handler class


class-pool . class ZCL_LOG_HANDLER definition public final create public . public section. methods HANDLE_MESSAGE for event LOGPOINT_ADDED of ZIF_LOGGABLE importing !IV_MESSAGE_TEXT !IV_MSG_TYPE !IV_EVENT_NAME !IV_OBJECT_NAME . endclass. "ZCL_LOG_HANDLER definition *"* local class implementation METHOD handle_message. WRITE iv_object_name. WRITE ' - '. WRITE iv_msg_type. WRITE ' - '. WRITE iv_event_name. WRITE ' - '. WRITE iv_message_text. NEW-LINE. ENDMETHOD. endclass. "ZCL_LOG_HANDLER implementation

For SAP internal use


P a g e | 46

21.6.3 Payroll function source code This report source code represents the payroll function. The classes for taxes are defined locally within the report (to keep the example simple).
*&---------------------------------------------------------------------* *& Report Z_TESTE2 *& *&---------------------------------------------------------------------* *& *& *&---------------------------------------------------------------------* REPORT z_test. TYPE-POOLS: abap. *----------------------------------------------------------------------* * CLASS lcl_tax_wt DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_tax_wt DEFINITION INHERITING FROM zcl_wt. PUBLIC SECTION. METHODS add_to_tax FOR EVENT wt_inserted OF zcl_rt IMPORTING ir_wt. ENDCLASS. "lcl_tax_wt DEFINITION

*----------------------------------------------------------------------* * CLASS lcl_state_tax_wt DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_state_tax_wt DEFINITION INHERITING FROM lcl_tax_wt. PUBLIC SECTION. METHODS add_to_tax REDEFINITION. PRIVATE SECTION. DATA mv_locked TYPE abap_bool. ENDCLASS. "lcl_state_tax_wt DEFINITION

*----------------------------------------------------------------------* * CLASS lcl_tax_wt IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_tax_wt IMPLEMENTATION. METHOD add_to_tax. DATA lv_tax TYPE maxbt. IF ir_wt->mv_lgart(2) = '/1'.

For SAP internal use


P a g e | 47
lv_tax = ir_wt->get_amount( ) * '0.2'. me->add_amount( lv_tax ). ENDIF. ENDMETHOD. ENDCLASS.

"add_to_tax "lcl_tax_wt IMPLEMENTATION

*----------------------------------------------------------------------* * CLASS lcl_state_tax_wt IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_state_tax_wt IMPLEMENTATION. METHOD add_to_tax . IF mv_locked = abap_true. RAISE EVENT logpoint_added EXPORTING iv_message_text = 'wagetype is locked and canot be modified' iv_msg_type = 'I' iv_event_name = 'add to tax' iv_object_name = mv_lgart. RETURN. ENDIF. super->add_to_tax( ir_wt ). IF me->mv_amount > 100. me->mv_amount = 100. RAISE EVENT logpoint_added EXPORTING iv_message_text = 'amount limited to 100 and locked' iv_msg_type = 'I' iv_event_name = 'add to tax' iv_object_name = mv_lgart. mv_locked = abap_true. ENDIF. ENDMETHOD. ENDCLASS. DATA DATA DATA DATA DATA "add_to_tax "lcl_state_tax_wt IMPLEMENTATION

lr_rt TYPE REF TO zcl_rt. lr_wt TYPE REF TO zcl_wt. lr_log TYPE REF TO zcl_log_handler. lr_tax_wt TYPE REF TO lcl_tax_wt. lr_state_tax_wt TYPE REF TO lcl_state_tax_wt.

START-OF-SELECTION.

CREATE OBJECT lr_rt. CREATE OBJECT lr_log. SET HANDLER lr_log->handle_message FOR ALL INSTANCES. CREATE OBJECT lr_tax_wt EXPORTING

For SAP internal use


P a g e | 48
iv_lgart = '/600' iv_amount = 0. SET HANDLER lr_tax_wt->add_to_tax FOR lr_rt.

CREATE OBJECT lr_state_tax_wt EXPORTING iv_lgart = '/650' iv_amount = 0. SET HANDLER lr_state_tax_wt->add_to_tax FOR lr_rt. "----------------------------CREATE OBJECT lr_wt EXPORTING iv_lgart = '/101' iv_amount = 1000. lr_rt->insert_wt( lr_wt ). "----------------------------CREATE OBJECT lr_wt EXPORTING iv_lgart = '/102' iv_amount = 2000. lr_rt->insert_wt( lr_wt ). "----------------------------CREATE OBJECT lr_wt EXPORTING iv_lgart = '/103' iv_amount = 3000. lr_wt->add_amount( 1000 ). lr_rt->insert_wt( lr_wt ). "adding taxes to RT: lr_rt->insert_wt( lr_tax_wt ). lr_rt->insert_wt( lr_state_tax_wt ).

22 Additional guidelines
22.1 Coding style
Program code must be readable and understandable by everyone who knows ABAP. If programs or part of programs are not understandable by reading the source code itself and its documentation, you must use comments. Comments should be in English and describe what a program, procedure, or part of a program is doing. If necessary, you should also comment on how the code produces it results, e.g., by including citations to literature if you implement general algorithms.

For SAP internal use


P a g e | 49 In addition to the naming rules prescribed by the syntax in Unicode-enabled classes (start a name with a letter followed by a sequence of letters, digits and underscores), we suggest no strict naming conventions for internal names. Nevertheless, we strongly recommend that names be meaningful to English-speaking readers and that you use naming conventions where appropriate to make coding clearer (see example in section 22.3). Respect the style of your colleagues. If you must change or maintain coding of other developers, you must respect the given coding style. For external names (repository objects) a small set of special naming conventions is valid that should not be used for other repository objects: o o o o o o CL_ for global classes IF_ for global interfaces CX_ for exception classes CL_OS_, IF_OS_, CX_OS _ for interfaces and classes of Object Services CL_BADI_, IF_BADI_, CX_BADI_ for interfaces and classes for BAdIs and so on

Use only the relational operators (=, <>, <, >, <=, >=), which are more readable than their respective old-fashioned character forms (EQ, NE, LT, GT, LE, GE). If available, use the addition NOT inside predicates of locigal expressions10 (e.g., dobj IS NOT INITIAL) instead of the Boolean operator NOT in front of the logical expression (e.g. NOT dobj IS INITIAL). The reason is that you naturally do the same for comparisons (dobj1 <> dobj2 instead of NOT dobj1 = dobj2), dont you? Use only the short form meth( ) for a method call. Use CALL METHOD meth for dynamic invocation only. With the short form you avoid the pollution of your source code with syntactical noise. This is especially important in object-oriented programming, where method invocations are more frequently used than, for example, function calls in procedural programming. Furthermore, you then use the same syntax for normal method calls as for functional method calls in operand positions. Use the keyword LENGTH len in type and data declarations with TYPES and DATA etc. instead of (len). The reason is to you use the same syntax in DATA and CREATE DATA. Use the equals sign (=) instead of TO and FROM when defining the parameter list in EXPORT and IMPORT. The reason is to use the same syntax for parameter lists as in all CALL statements.

In ABAP a logical expression, i.e., an expression whose result is true or false, can be written either as comparisons involving relational operators (=, <>, ) or with predicates involving special language elements (BETWEEN, IS, ). Other than the language element NOT inside the predicates of a logical expression, the Boolean Operators NOT works with the result of the expression.

10

For SAP internal use


P a g e | 50

This section gives you some recommendations on the usage of ABAP Objects, how to modularize your program and how to control the program flow. Keep an eye on the memory consumption of objects. Release unneeded objects to the garbage collector the garbage collector can only delete an object if the object is no longer referenced. Be aware that references to objects can exist in various contexts, e.g. in local fields of methods, in instance attributes of an object, in static attributes of classes, in global data objects of the program, and last but not least in event handler tables or frameworks. In the latter case be aware that eventually you must use special methods of the respective framework to free objects.11

22.2 ABAP Objects, Modularization, and Program Flow

22.3 Some General Hints on Modularization


Context
If ABAP Objects become your recommended programming model, methods will naturally become the principal instrument for modularizing your program. Although the following recommendations regarding method implementation have a more general, rather than ABAP-specific character, well nevertheless not refrain from presenting these as ABAP coding guidelines.

Recommendation

Every method must be a coherent unit of work. Some very general guidelines for implementing methods are: Short is better than long Modularize dont atomize. Flat is better than deep Avoid potential side-effects From these, more practical recommendations can be derived: One-line methods should be an exception. As a rule of thumb, the number of lines should normally not be less than five, the maximum number of lines should not exceed 50, and the number of declarative statements should not exceed ten. As a broader rule of thumb, a method should fit on a single printed page. The cyclomatic number of a method (number of branches due to tests) should not be greater than ten12

In this context its worthwhile to call attention to system class CL_ABAP_WEAK_REFERENCE. Objects of that class represent weak references to an object. Unlike normal object references, a weak reference does not prevent the referenced object from being deleted when the garbage collector is executed. You might use this class if you need to create applications that simply monitor certain kinds of objects without keeping the monitored objects alive. An example for the need of such applications is the internal administration and monitoring of ABAPs shared objects in the shared objects memory. 12 When computing this number we use a very simplistic approach we dont count the WHEN conditions of a CASE structure or the ELSEIF conditions of an IF structure as individual tests, but each CASE or IF structure is counted as a single test. We are aware that the number of WHEN or ELSEIF conditions also increases method complexity and that a cyclomatic number of 10 without consideration of these conditions is a very rough criterion only.

11

For SAP internal use


P a g e | 51 Be careful when you modify data that are global for a method inside the method. Possible non-local data that can be accessed directly for writing in a method are (where directly means not via a reference): o o o All attributes of the own class including the protected attributes of super classes. A method can even write to private instance attributes of other objects of the same class.13 Public attributes of other classes that are not declared as READ-ONLY.

All global data of the ABAP program. The declaration of global data should be avoided under any circumstances. Nevertheless, there are exceptions from this rule as the interface work areas for classic dynpros. Since operational coding is allowed in methods only, it is clear that you must modify data that are global to methods within methods. In order to keep your methods free of side effects, avoid the careless modification of such global data in too many methods. Use only dedicated methods that can be recognized, e.g., via their naming (set_... methods). This is especially important for data that do not belong to the own class. For example, the interface work area for a dynpro should be set in one dedicated method of one dedicated class and only before calling the dynpro. When you access data that are global for a method inside the method it is a good idea to distinguish them from the methods local data via the appropriate selectors: o For attributes of the own class including the protected attributes of super classes you can use the self reference me and the object component selector (->) for instance attributes or the class name and the class component selector (=>) for static attributes. Public attributes of other classes must be addressed via reference variables and object component selector (->) or class name and class component selector (=>) anyway.

o o

Global data of the program cannot be addressed with special selectors. Therefore they can be easily confused with attributes of the own class or with local data of the method. This is an additional reason not to access them in too many methods (see example below). Although we have not suggested strict naming rules, some naming conventions can also be helpful here. From the foregoing, a general rule emerges: you should restrict the scope of a method to a specific task. Refrain from working with too many attributes of the class or even global data inside a single method, otherwise the methods implementation can quickly become confusing (see example below). Note Never call a procedure without making provision for appropriate exception handling (see section 0).

Example

The following example shows the addressing of data objects within a method in a chained WRITE statement14. The identifiers a1 and a2 address the respective local data objects of method main.

The level of encapsulation in ABAP Objects is the class, not the object. Note that we use WRITE for list output here, in seeming contradiction of our recommendations in section 2.1.2. However, for simple tests and non-productive coding, WRITE can still play the same role in ABAP as, for example, System.out.println(...) does in Java.
14

13

For SAP internal use


P a g e | 52 demo=>a1, me->a2, and a3 address the attributes of the class. a4 addresses a global data object of the program. The global data objects a1, a2, and a3 cannot be accessed from inside the method. Now imagine that you see only the method because it is inside a INCLUDE program or because you have a printout. From simply looking at the method you couldnt tell whether a3 and a4 are attributes of the class or global data. In the ABAP Workbench you can double-click the name to find out the place of declaration, but if you have only a printout, you are lost. Therefore, a3 should be written as me->a3 too. Also a naming convention for global data, e.g. g_a1, g_a2, can be helpful here.
PROGRAM ... DATA DATA DATA DATA a1 a2 a3 a4 TYPE TYPE TYPE TYPE string string string string VALUE VALUE VALUE VALUE `a1 `a2 `a3 `a4 global`. global`. global`. global`.

CLASS demo DEFINITION. PUBLIC SECTION. METHODS main. CLASS-DATA a1 TYPE string VALUE `a1 class`. DATA a2 TYPE string VALUE `a2 class`. DATA a3 TYPE string VALUE `a3 class`. ENDCLASS. CLASS demo IMPLEMENTATION. METHOD main. DATA a1 TYPE string VALUE `a1 local`. DATA a2 TYPE string VALUE `a2 local`. WRITE: / a1, / a2, / demo=>a1, / me->a2, / a3, / a4. ENDMETHOD. ENDCLASS.

22.4 Declaration of the Instance Constructor


You know that the visibility of all components of a class must be defined by declaring each component in a visibility area (PUBLIC SECTION, PROTECTED SECTION, PRIVATE SECTION)15. The same seems to hold for the declaration of the instance constructor that is declared with METHODS constructor. Unfortunately, this is not true. The true visibility of an instance constructor is declared with the CREATE addition of CLASS DEFINITION. By writing PUBLIC, PROTECTED or PRIVATE behind CREATE in the CLASS statement, you denote who can create objects of that class using CREATE OBJECT and, therefore, who can invoke the instance constructor. The declaration of the instance constructor with METHODS constructor within a visibility section is for syntactical reasons only (it must be declared somewhere). Up until SAP NetWeaver 04, the instance constructor could be declared in the public visibility section only. As of SAP NetWeaver 2004s, the
The visibility area defines the visibility of the components of a class and thus also the interfaces between the class and its users. For a more detailed discussion of this concept, see Horst Keller, The Official ABAP Reference Vol. I (New York, NY: SAP Press 2005).
15

Context

For SAP internal use


P a g e | 53 instance constructor can be declared in all visibility sections, that are the same or more general than the visibility for instantiation defined via the CREATE addition of CLASS DEFINITION. But remember, the true visibility is always defined by the CREATE addition.

Declare the instance constructor always in the visibility section that matches its true visibility. For a class declared with CREATE PRIVATE, the statement METHODS constructor should be placed in the PRIVATE SECTION, for CREATE PROTECTED in the PROTECTED SECTION and FOR CREATE PUBLC in the PUBLIC SECTION. Then: The visibility section of the instance constructor protocols its technical visibility. You can refer to declarations of the same section in the interface of the constructor

Recommendation

Example
The following example shows the recommended declaration of the instance constructor in a class that is declared with CREATE PRIVATE as of SAP NetWeaver 2004s. Note that a declaration in the PROTECTED or PUBLIC section is also allowed (up until SAP NetWeaver 04, only the declaration in the PUBLIC section was allowed), but that you cannot not use a private type for typing the formal parameter then.
CLASS demo DEFINITION CREATE PRIVATE. PUBLIC SECTION. ... PROTECTED SECTION. ... PRIVATE SECTION. TYPES t_... TYPE ... METHODS constructor IMPORTING p TYPE t_... ENDCLASS. CLASS demo IMPLEMENTATION. METHOD constructor. ... ENDMETHOD. ... ENDCLASS.

Parameter interfaces When defining the parameter interface of a method (or a function module), keep the following points in mind: Regarding the number of formal parameters, as a rule the parameter interface of a procedure should be slim. Ideally, a method is functional, i.e., it has no or only few importing parameters and one returning parameter. Regarding the kind of formal parameters, always use the appropriate kind. The kind of parameter defines the semantics of a parameter, while the way of passing is a more technical detail that should not be mixed up with the parameters kind. For example, do not exploit the fact that an EXPORTING parameter passed by reference behaves like a CHANGING parameter. If you need an Input/Output parameter, define it as a CHANGING parameter and use EXPORTING parameters for output only (do not read it before it has first been written).

For SAP internal use


P a g e | 54 For the way of parameter passing, you must weigh performance vs. robustness. While passing by reference may provide better performance (especially for large parameters), passing by value is more robust. For example: o An EXPORTING parameter passed by reference is not initialized during the call (it behaves like a CHANGING parameter). Therefore, it is dangerous to read an EXPORTING parameter passed by reference before it has first been written. From the semantics of the parameter you would expect it to be initial, but it contains the actual parameters value. Also, when you insert lines into internal tables or concatenate contents to byte or character strings that are defined as such parameters, you must not forget to initialize the parameter first. Another potential trap is that write access to EXPORTING and CHANGING parameters passed by reference works directly on the actual parameters. If the procedure is ended with an exception, the changes will not be undone make sure that this is what you intend. Even for IMPORTING parameters that are passed by reference unexpected effects can occur if the procedure changes either explicitly or implicitly the actual parameter. In such an instance, the IMPORTING parameter will be changed even though it is protected from writing, from the methods point of view. This point is particularly problematic when passing system fields (see Example 1 below).

22.5 Control Structures


If possible, follow the SESE principle (Single-Entry/Single-Exit) for control structures (not for procedures); i.e., a control structure must be exited either normally or via a statement (EXIT, RETURN, LEAVE), but should not provide for exit using both techniques. Avoid excessive block nesting depth (e.g., IF, LOOP, WHILE, ...). As a rule of thumb, the nesting depth level should not exceed a depth of 3. Dont use the statements CHECK or EXIT outside of loops to exit processing blocks use RETURN instead. CHECK and EXIT are context-sensitive; inside a loop they exit the loop, outside a loop they exit the current processing block. RETURN always exits the current processing block and is the dedicated statement for this purpose. Dont use the additions VARYING/VARY in DO/WHILE loops. Become comfortable with the use of the modern alternatives ASSIGN COMPONENT and ASSIGN INCREMENT. The reason is that ASSIGN and its additions offer a set of general functions for accessing sequences of memory areas that should be preferred, since they allow data manipulation in-place, rather than requiring data movement. In this context, please note that the usage of the addition INCREMENT with ASSIGN is dedicated for accessing sequences of equidistant memory areas that have the same data type. Other applications can result in runtime errors and in different behavior in Unicode and non-Unicode systems.

For SAP internal use


P a g e | 55

Lets assume a structure text with at least four subsequent text-like components word1 to word4 that have the same length. The first DO loop accesses these components using the obsolete VARYING addition. Here, a data object word is used to access the contents of the components. If the structure text contains less than four of these components, the behavior can be undefined or even a runtime error might occur, if the range of text is exceeded. DO 4 TIMES VARYING word FROM text-word1 NEXT text-word2. WRITE / word. ENDDO. The second DO loop shows how the functions of the first loop can be programmed more explicitly and more generically using the statement ASSIGN INCREMENT. Here, a field symbol <word> is used to point to the components instead of copying their contents to a variable. The success of the assignment can be checked in sy-subrc and the loop is exited when the memory range of text is exceeded. DO 4 TIMES. idx = sy-index - 1. ASSIGN text-word1 INCREMENT idx TO <word> RANGE text. IF sy-subrc <> 0. EXIT. ENDIF. WRITE / <word>. ENDDO. For the second DO loop we assumed that the name but not the position of the first component in the series of four is known. If you know the position pos of the first component in the structure, you might also use the following DO loop using the statement ASSIGN COMPONENT. DO 4 TIMES. idx = pos + sy-index - 1. ASSIGN COMPONENT idx OF STRUCTURE text TO <word>. IF sy-subrc <> 0. EXIT. ENDIF. WRITE / <word>. ENDDO.

Examples

22.6 Handle Exporting Parameters Passed by Reference Appropriately

Priority Recommended Rule You should not access an EXPORTING parameter passed by reference for reading before a write access. If the first write access does not replace the contents completely you should initialize the parameter before writing to it. Rationale Before the first write access, the contents of an EXPORTING parameter passed by reference is undefined (it is the contents of the actual parameter bound to the formal parameter). Known Problems None Exceptions

For SAP internal use


P a g e | 56 None Bad Example CLASS cl_class DEFINITION ... PUBLIC SECTION. METHODS meth EXPORTING ev_p TYPE STANDARD table. ENDCLASS. CLASS cl_class IMPLEMENTATION. METHOD meth. APPEND ... TO ev_p. ENDMETHOD. ENDCLASS. Good Example ... METHOD meth. CLEAR ev_p. APPEND ... TO ev_p. ENDMETHOD. Priority Mandatory Rule You must use RETURN if you want to leave a method before reaching ENDMETHOD. You must not use CHECK or EXIT outside of loops. Rationale RETURN always exits the current processing block and is the dedicated statement for this purpose. CHECK and EXIT are context-sensitive. Inside a loop they exit the loop, outside a loop they exit the current processing block. Known Problems CHECK statements can define a kind of a pre-condition block in a method that is better readable than an IF block. A conditional RETURN is required. Exceptions A CHECK statement can be exceptionally used outside of a loop, if it is the very first statement of the method. Bad Example METHOD meth. ... CHECK continue_flag = abap_true. ... ENDMETHOD. Good Example METHOD meth. ... IF continue_flag <> abap_true. RETURN. ENDIF. ... ENDMETHOD.

22.7 Jump Out of Methods Only via RETURN

For SAP internal use


P a g e | 57

Priority Recommended Rule You should use only the short form meth( ) for a method call. Use CALL METHOD meth for dynamic invocation only. Rationale This avoids the pollution of your source code with syntactical noise. Usage of the same syntax for normal method calls as for functional method calls in operand positions. Known Problems None Exceptions None Bad Example CALL METHOD meth EXPORTING i1 = lv_e1 i2 = lv_e2 IMPORTING o1 = lv_i1 o2 = lv_i2 CHANGING c1 = lv_c1 c2 = lv_c2. Good Example meth( EXPORTING i1 = lv_e1 i2 = lv_e2 IMPORTING o1 = lv_i1 o2 = lv_i2 CHANGING c1 = lv_c1 c2 = lv_c2 ).

22.8 Do not use CALL METHOD for static invoke

22.9 Type and Data Declarations


Data Types Declare New Data Types as Standalone Data Types with Semantic Meaning Priority Recommended Rule You should construct a new data type only as a standalone data type and not as a bound data type, especially if you intend to have more than one variable using this new data type. Avoid the definition of purely technical types. Rationale A standalone type can be maintained centrally, reused at different places, and the definition can be easily transferred to more general contexts if needed. A standalone type carries per se a semantic meaning. Known Problems Declaration overhead. Exceptions Local data type that is used by one variable only. Bad Example DATA: word1 TYPE c LENGTH 10, word2 TYPE c LENGTH 10.. Good Example TYPES word_10 TYPE c LENGTH 10. DATA: word1 TYPE word_10, word2 TYPE word_10.

For SAP internal use


P a g e | 58 Declare Data Types in the Appropriate Context Priority Hint Rule You should declare types in the following contexts: Declare local types inside methods for helper variables whose technical settings are not defined by more general types of the same application. Declare global types (and constants) in the public sections of global classes or in global interfaces. You should not create new type pools. Never use shared includes to declare data types in includes for reuse in several programs. You should declare types in the ABAP Dictionary only when you need the features that are offered by the Dictionary, as for example when defining types for classic dynpro fields or structures for database tables. Rationale Data types (and constants) in global classes or in global interfaces are creates in the semantic context and hence automatically in the package where you need them. Types in the ABAP Dictionary are somewhat free-floating. The ABAP Dictionary is swamped with copious quantities of types that are needed for special purposes only. Known Problems Developers tend to forget to document types in classes or interfaces as good as types in the ABAP Dictionary. Some developers claim, that the documentation of the semantic meaning of types in classes or interfaces cannot be reached easily enough. Prior to Release 7.1, the ABAP Compilers capability for treating intertwined type declarations in classes and interfaces was somewhat limited or even prone to errors. These problems are resolved with Release 7.1. Exceptions Before Release 7.1: Syntax errors and performance problems due to flaws in the ABAP compiler Bad Example CLASS cl_class DEFINITION ... PRIVATE SECTION. DATA xmltab TYPE xml_tab. XML_TAB defined in dictionary ENDCLASS. Good Example CLASS cl_class DEFINITION ... PRIVATE SECTION. TYPES x255 TYPE x LENGTH 255. TYPES xml_tab TYPE STANDARD TABLE OF x255. DATA xmltab TYPE xml_tab. ENDCLASS.

22.10 Reuse Only Data Types that Exactly Match Your Needs
Priority Strongly Recommended Rule

For SAP internal use


P a g e | 59 You should never reuse a data type because of its technical settings only. You should always reuse data types according to their semantic meaning. Especially you should never reuse the definition of database tables on classic dynpros. Rationale Separation of concerns. The rationale behind standalone data types is mainly their semantic meaning. Known Problems We are missing an official and documented repository that publishes commonly needed data types where the semantics is defined by the technical settings as e.g. CHAR255. Exceptions None Bad Example DATA text TYPE s_url. Good Example DATA text TYPE char255.

22.11 Use the data type ABAP_BOOL as Boolean data type

Priority Recommended Rule You should refer to ABAP_BOOL from type group ABAP for working with Boolean fields instead of declaring your own data type. As values you should use the predefined constants ABAP_TRUE and ABAP_FALSE from the same type group. Rationale ABAP_BOOL is the central data type that is foreseen for Boolean values. Known Problems None Exceptions None Bad Example DATA flag TYPE c LENGTH 1. IF flag = 'X'. ... ENDIF. Good Example TYPE-POOLS abap. DATA flag TYPE abap_bool. IF flag = abap_true. ... ENDIF.

22.12 Declare Data Objects Only in Classes

Priority Mandatory Rule You must declare data objects only as attributes of classes or as local helper variables in methods. Rationale Only data objects in classes of ABAP Objects are encapsulated properly.

For SAP internal use


P a g e | 60 Known Problems Interface working areas for classic dynpros and parameters or selection tables for selection screens can be declared only outside of classes in the global declaration part of an ABAP program. Exceptions Interface working areas for classic dynpros (defined by TABLES) and parameters or selection tables for selection screens (defined by PARAMETERS and SELECTION-SCREEN) if needed. When you define such global data objects outside of classes, you should always follow the stricter syntax rules of ABAP Objects. For example, declare only internal tables without header lines as working areas for table controls on Dynpros. Priority Strongly recommended Rule You should never use the TABLES statement except when declaring an interface to classic Dynpros. For each dynpro, declare a respective structure in the ABAP Dictionary that contains the definitions and texts for all fields of the dynpro. Especially do not use the TABLES statement with the name of a database table any more. Instead, you use the structures that are especially defined for dynpros or groups of dynpros. Rationale All other usages of TABLES are no longer required. Known Problems None Exceptions None Bad Example TABLES dbtab. Good Example TABLES appl_screen_100. Priority Recommended Rule You should use LIKE to refer to other data objects in all cases where a declaration is directly dependent on an existing data object or its components. In all other cases, you should use TYPE to refer to the standalone data types directly. Rationale Program remains valid even if the data type of the data object you are referring to is changed. Known Problems None Exceptions None Bad Example CLASS cl_class DEFINITION ...

22.13 Use the TABLES Statement Only for Dynpro Structures

22.14 Refer to Data Objects when Appropriate, refer to Data Types Otherwise

For SAP internal use


P a g e | 61 PUBLIC SECTION. METHODS meth IMPORTING iv_p TYPE ddic_type. ENDCLASS. CLASS cl_class IMPLEMENTATION. METHOD meth. DATA lv_p TYPE ddic_type. Good Example CLASS cl_class IMPLEMENTATION. METHOD meth. DATA lv_p LIKE iv_p.

23 Error Handling

Software development and operation and ABAP is of course no exception here are intrinsically prone to errors in a number of ways: Internal errors may arise from incorrect implementations and improper usage of underlying services. When interacting with external resources (such as the user, system memory, or disk storage), errors can occur due to invalid input or unexpected resource limitations. The ABAP language and infrastructure offers different ways for handling these kinds of errors, both those that are recoverable (exceptions and dialog messages) and those that are non-recoverable (assertions and exit messages). Exceptions

Context

ABAP offers different ways to handle recoverable errors: Class-based (new) exceptions Class-based exceptions are available as of release 6.10. They replace classic exceptions and catchable runtime errors. A class-based exception is a treatable (i.e., capable of being caught) exception, which is represented by an exception object of an exception class.16 If an exception occurs, an object of an exception class is generated17 and can be propagated across call levels. The propagation behavior depends on the kind of exception class. (See sidebar: Kinds of Exception Classes). There are predefined exception classes for exceptions of the runtime environment, and selfdefined exception classes for custom applications. A class-based exception that is handled in the program does not lead to a runtime error. A class-based exception can be handled with a CATCH statement if it occurs in the TRY block of the respective TRY ENDTRY control structure.18
An exception class is a class that inherits from the predefined abstract class CX_ROOT. For performance reasons, the object is in fact only generated when its actually addressed during exception handling in the program. 18 For further information on this subject, see A Programmer's Guide to the New Exception-Handling Concept in ABAP (SAP Professional Journal, September/October, 2002)
17 16

For SAP internal use


P a g e | 62 Classic (old) exceptions Classic exceptions can be defined in the interfaces of function modules and methods. The treatment of classic exceptions is made possible by the addition EXCEPTIONS of the statements CALL METHOD and CALL FUNCTION. Numeric values are assigned to the exceptions, which are used to fill the system field sy-subrc when the exception occurs. The actual error handling takes place after the call, when your program evaluates sy-subrc. Catchable runtime errors Before release 6.10, treatable exceptions for error situations of the runtime environment were defined exclusively as catchable runtime errors. All catchable runtime errors are predefined in the system and assigned to the ABAP statements during the execution of which they may occur. Several runtime errors can be grouped together in an exception group and can be handled together under the name of the group. The statement for handling catchable runtime errors before Release 6.10 was CATCH SYSTEM-EXCEPTIONS. As of release 6.10, an exception class is assigned to each catchable runtime error and they can be handled as class-based exceptions.

Recommendation

As a rule, use mainly class-based exceptions. The other exceptions (classic exceptions, catchable runtime errors) are only there for backwards compatibility and are mostly superseded by class-based exceptions. Do not define classic exceptions in methods or function modules any longer. Classic exceptions still play a role in existing procedures, so you will have to deal with them when calling or modifying such procedures. It is good practice to catch classic exceptions and map them to class-based exceptions. Consider the use of MESSAGE RAISING instead of RAISE in existing procedures that are governed by classic exceptions. With MESSAGE RAISING you can pass textual information about the situation to the handler of the exception. Therefore, such messages can be seen as a precursor to exception texts in exception classes. Catchable runtime errors are now completely obsolete, since they have been replaced by corresponding exception classes. Instead of CATCH SYSTEM-EXCEPTIONS use TRY ... CATCH ... ENDTRY only. In the following, we will give you some hints on working with class-based exceptions. Creating exception classes If you discover a situation where your program might create an error adhere to the following procedure: 1. Check if an exception is appropriate In a situation where an exception is too strong, alternatives to exceptions are selfdefined return codes or flags, for example showing if a read operation was successful or not. In a situation where a treatable exception is too weak, e.g., when no recovery is possible, alternatives to exceptions are assertions (see 23.1) or exit messages (see 23.2). 2. Describe the situation

For SAP internal use


P a g e | 63 If an exception is justified, describe its important characteristics. The description should contain more semantic than technical information (as for example the place in the code). Violations of this rule can hinder the reuse of an exception and make handling more difficult, if the exception is propagated. 3. Check for an existing exception class Search for an existing exception class with texts that describes the situation well, that is of the right type (see sidebar: Kinds of Exception Classes), and has appropriate attributes. If an exception class has more attributes than needed, refrain from using it. 4. Refine an existing exception class If you found an exception class that almost fits, think about refining it. If the exception text does not to describe the exact situation, define an additional text. If you need additional attributes, consider inheriting from the existing exception class. But only do so, if your error situation is really a special situation of the general error described by the super class.19 5. Create a new exception class If you havent found a reusable exception class, define a new one. The most important decision is to determine the right exception type: static check, dynamic check or no check. (see sidebar : Kinds of Exception Classes)

There are three kinds of exception classes: static check, dynamic check or no check, which are defined by the class super class. Static check exceptions The exception classes inherit from CX_STATIC_CHECK. Such exceptions can be propagated from a procedure, if they are explicitly declared in the interface of the procedure. This is checked during compile time. Use this kind of exception if you want the caller of a procedure to explicitly take care of the error situation. If the exception can safely be avoided by additional checks beforehand it might be better to define a dynamic check exception. Dynamic check exceptions The exception classes inherit from CX_DYNAMIC_CHECK. For propagation from procedures, the same holds as for static check exceptions with the difference, that the declaration is not checked at compile time but only after an exception has actually occurred at runtime. Use this kind for exceptions a caller usually can avoid either because they know that it cant possibly occur or that an appropriate check has been made before calling a certain routine. It is a good idea to document such preconditions for procedures. In a properly designed application, most exceptions will probably be of type dynamic check. No check exceptions The exception classes inherit from CX_NO_CHECK. Such exceptions can be propagated from a procedure without explicit declaration in the interface of the procedure. Use this kind of exception for errors where you cannot expect a caller to handle it directly. Typical examples are
19

Begin Sidebar: Kinds of Exception Classes

Before inheriting from an exception class, in some cases it might even be useful to first create a new super class for related existing exception classes and to inherit the existing classes and your new one from that super class.

For SAP internal use


P a g e | 64 exhausted resources, configuration problems, etc. Normally there should be not many situations in an application program where a no check exception is appropriate. Handling class-based exceptions The general rule is to catch selectively. A handler should usually only catch exceptions it can handle properly and propagate all others to the next level of the call stack. Catching exceptions by specifying the abstract root classes of the exception tree (CX_ROOT, CX_STATIC_CHECK, CX_DYNAMIC_CHECK, CX_NO_CHECK) is almost never a good idea, except at the highest levels of a software architecture, e.g., in some global dispatcher. Propagating class-based exceptions Exceptions that can be propagated from a procedure are part of the procedures parameter interface. The interface should describe these exceptions from the callers point of view, not the implementers. When you propagate exceptions in a call stack, you generally move from lower, more basic, levels up to higher, more abstract levels. You should support this abstraction also when propagating exceptions by mapping implementation-specific exceptions to exceptions that are understood by the caller. Technically this means that you catch the implementation specific exceptions and raise new exceptions that are a suitable abstraction. In order to reconstruct the chain of causation for the new exception it is good practice to pass the old exception to the attribute PREVIOUS of the new one. Note Each exception changes the normal flow of control and may present a serious risk for the consistency of an application. Therefore, be aware of the possibility of a CLEANUP clause in a TRY block. If necessary, use it to bring your application back into a consistent state.

End of Sidebar

23.1 Assertions
Context
Assertions are closely related to error handling; with assertions you express conditions that must hold true. They can be checked at runtime and if they fail an error has been found. Assertions are available in ABAP as of Release 6.20, Support Package 29 via the ASSERT statement. An assertion is either always active or can be activated via the maintenance transaction SAAB. When a program reaches an active assertion, it evaluates the corresponding condition. If the condition is violated the program terminates with a runtime error, accesses the ABAP Debugger, or creates a log entry. The behavior is controlled by the activation settings.

Recommendation

Since assertions can be activated from outside a program and inactive assertions produce no additional load on an application, use them frequently. But do not use an assertion when actually an exception is required. Impose assertions only on conditions for program states that are fully under your control. Never formulate an assertion which is based on input parameters or the availability of some external resource. The violation of such conditions must result in an exception so that an external caller can catch it and react to it. Assertions must be always implementation-specific. Only preconditions for internally used functionality might be checked by assertions (e.g., some private method of a class, which only you can use in your implementation). Assertions are typically used for invariants and post-conditions where you are simply stating that your implementation does what you specified.

For SAP internal use


P a g e | 65 If a productive system is in a normal state, activatable assertions are normally switched off. If the checking is always crucial you can use always active assertions. If the condition of an always active assertion is violated, the program terminates with the runtime error ASSERTION_FAILED. If you want a caller to be able to react in such a case, you must use an exception instead of an assertion. Note Logging provides a mechanism complementary to error handling with exceptions or assertions. While exceptions or assertions focus on changing the control flow in case of a locally detected error, logging addresses a broader view by leaving traces in critical situations. This enables a global analysis of system behavior, where the single events are put into a temporal context. If logging is necessary, use a framework that is generally available, as, e.g. the so-called Application Log, which is based on function modules starting with BAL_. Theres no need for you to create your own logging environment as of SAP NetWeaver 2004s, there is also a dedicated ABAP statement LOG-POINT for logging purposes.

23.2 Messages
Context
Messages are basically texts that can be displayed via the MESSAGE statement as so-called dialog messages during screen (dynpro) processing. Messages can be classified with a message type that defines the display type and the program behavior (message handling). Possible message types are termination message (A), error message (E), information message (I), status message (S), or warning (W). On the application logic side, messages can be used as a surrogate for exceptions that are combined with a text. This is made available by the predefined classic exception ERROR_MESSAGE in function modules or by using the RAISING addition with the MESSAGE statement. Furthermore, there is a special message type X. When classified with X, a message is an exit message that terminates a program via a runtime error.

Messages are mainly intended for use during classic dialog processing involving dynpros, and especially during the event PAI. During PAI processing, messages of type E and W permit an error dialog when used in connection with the dynpro statement FIELD. Therefore, never use messages and in particular messages of type E or W in contexts other than PAI processing. On the application logic side, do not use messages for exceptions use class-based exceptions instead (see section 0). If existing procedures send messages, it is good practice to catch and map them to classbased exceptions. If an exception should be connected to an end-user specific dialog text, you can implement the predefined interface IF_T100_MESSAGE in the exception class (as of SAP NetWeaver 04). After catching such an exception using TRY ... CATCH ... ENDTRY you use the exception object directly in the MESSAGE statement to get a proper error message during dialog processing. Since they terminate an application without any possibility of recovery, use exit messages of type X only with extreme care. In most cases throwing an exception or using an assertion is the better approach (see 0 for when to use which of these). Cases for exit messages could be the prevention of database inconsistencies or fatal errors in the user interface. In such circumstances, an exit message might be favored over assertions because it allows you to display a meaningful message text in the short dump resulting from the runtime error. For basic application functionality that is widely reused, however, never use exit messages, since they can hinder the reuse of parts of your application to a great extent.

Recommendation

You might also like