OOP ABAP Part 8: Interfaces and Factory Methods
Create objects in one controlled place: static factory methods, CREATE PRIVATE, selecting an implementation from customizing, the singleton pattern, and how SAP itself uses factories (CL SALV TABLE= FACTORY, BAdI GET BADI).
Explanation
📌 KEY POINTS
✅ A factory method creates and returns objects typed as an interface; callers never write CREATE OBJECT for the concrete class.
✅ The factory decides which implementation to create (by parameter, customizing or country).
✅ CREATE PRIVATE + static factory = the only way to get an instance (used for singletons).
⚠️ Singletons keep state for the whole session — use them only for truly session-wide objects.
━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE
Parts 3 and 7 created implementations directly in the report. Real programs should not know all concrete classes — a factory hides that decision.
━━━━━━━━━━━━━━━━━━━━
▶ 1. WHY FACTORIES
- Callers depend only on the interface (ZIF_…).
- Adding an implementation changes only the factory.
- Creation logic (validation, reading customizing, caching) lives in one place.
- Tests can make the factory return a test double (Part 9).
━━━━━━━━━━━━━━━━━━━━
▶ 2. STATIC FACTORY METHOD
CLASS-METHODS create IMPORTING iv_country TYPE land1 RETURNING VALUE(ro_tax) TYPE REF TO zif_tax_calculator.
Inside: CASE iv_country → CREATE OBJECT ro_tax TYPE zcl_tax_in / zcl_tax_de / zcl_tax_default.
Note the syntax CREATE OBJECT ref TYPE class — the reference is interface-typed, the object is concrete.
━━━━━━━━━━━━━━━━━━━━
▶ 3. CUSTOMIZING-DRIVEN SELECTION
Instead of a CASE, a Z-table maps country → class name. The factory reads it and uses dynamic CREATE OBJECT ro_tax TYPE (lv_classname). New countries need only a new class and a table entry. Catch CX_SY_CREATE_OBJECT_ERROR for wrong names.
━━━━━━━━━━━━━━━━━━━━
▶ 4. SINGLETON
A class with CREATE PRIVATE, a private static attribute go_instance and a static get_instance( ) that creates it once. Typical for session-wide buffers or loggers. Downsides: global state, harder tests.
━━━━━━━━━━━━━━━━━━━━
▶ 5. SAP'S OWN FACTORIES
- cl_salv_table=>factory( IMPORTING r_salv_table = … CHANGING t_table = … ) — ALV objects.
- GET BADI — the enhancement framework picks the active implementations.
- cl_abap_typedescr=>describe_by_data( ) — RTTI.
- cl_bcs=>create_persistent( ) — send requests.
━━━━━━━━━━━━━━━━━━━━
▶ 6. FACTORY VS DEPENDENCY INJECTION
A factory decides what to create; dependency injection (Part 9) decides who passes it in. They work together: the factory creates, the constructor receives.
━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP
- Run the code example with countries IN and DE.
- Add a US class and extend only the factory.
- Replace the CASE with a dynamic class name and try an invalid name.
💡 Modern (7.40+): ro_tax = NEW lcl_tax_in( ) for static creation; dynamic creation by class name still uses CREATE OBJECT … TYPE (lv_classname).
✅ CHECKPOINT: callers ask a factory for an interface, and implementations can be added without touching them.
Code example
REPORT zoop_factory. INTERFACE lif_tax_calculator. METHODS calculate IMPORTING iv_net TYPE s_price RETURNING VALUE(rv_tax) TYPE s_price.ENDINTERFACE. CLASS lcl_tax_in DEFINITION FINAL. PUBLIC SECTION. INTERFACES lif_tax_calculator.ENDCLASS.CLASS lcl_tax_in IMPLEMENTATION. METHOD lif_tax_calculator~calculate. rv_tax = iv_net * '0.18'. ENDMETHOD.ENDCLASS. CLASS lcl_tax_de DEFINITION FINAL. PUBLIC SECTION. INTERFACES lif_tax_calculator.ENDCLASS.CLASS lcl_tax_de IMPLEMENTATION. METHOD lif_tax_calculator~calculate. rv_tax = iv_net * '0.19'. ENDMETHOD.ENDCLASS. CLASS lcl_tax_default DEFINITION FINAL. PUBLIC SECTION. INTERFACES lif_tax_calculator.ENDCLASS.CLASS lcl_tax_default IMPLEMENTATION. METHOD lif_tax_calculator~calculate. rv_tax = 0. ENDMETHOD.ENDCLASS. * ===== Factory: the ONLY place that knows concrete classes =====CLASS lcl_tax_factory DEFINITION FINAL CREATE PRIVATE. PUBLIC SECTION. CLASS-METHODS create IMPORTING iv_country TYPE land1 RETURNING VALUE(ro_tax) TYPE REF TO lif_tax_calculator.ENDCLASS. CLASS lcl_tax_factory IMPLEMENTATION. METHOD create. CASE iv_country. WHEN 'IN'. CREATE OBJECT ro_tax TYPE lcl_tax_in. " interface ref, concrete object WHEN 'DE'. CREATE OBJECT ro_tax TYPE lcl_tax_de. WHEN OTHERS. CREATE OBJECT ro_tax TYPE lcl_tax_default. ENDCASE. ENDMETHOD.ENDCLASS. * ===== Singleton: one instance per session =====CLASS lcl_logger DEFINITION FINAL CREATE PRIVATE. PUBLIC SECTION. CLASS-METHODS get_instance RETURNING VALUE(ro_logger) TYPE REF TO lcl_logger. METHODS add IMPORTING iv_text TYPE string. PRIVATE SECTION. CLASS-DATA go_instance TYPE REF TO lcl_logger. DATA mt_log TYPE STANDARD TABLE OF string.ENDCLASS. CLASS lcl_logger IMPLEMENTATION. METHOD get_instance. IF go_instance IS NOT BOUND. CREATE OBJECT go_instance. ENDIF. ro_logger = go_instance. ENDMETHOD. METHOD add. APPEND iv_text TO mt_log. ENDMETHOD.ENDCLASS. PARAMETERS p_land TYPE land1 DEFAULT 'IN'. DATA: lo_tax TYPE REF TO lif_tax_calculator, lv_tax TYPE s_price, lv_classname TYPE seoclsname VALUE 'LCL_TAX_DE'. START-OF-SELECTION. lo_tax = lcl_tax_factory=>create( p_land ). " caller knows only the interface lv_tax = lo_tax->calculate( '1000' ). WRITE: / 'Tax for', p_land, ':', lv_tax. lcl_logger=>get_instance( )->add( |Tax calculated for { p_land }| ). * ===== Dynamic creation from a class name (e.g. read from a Z customizing table) ===== TRY. CREATE OBJECT lo_tax TYPE (lv_classname). lv_tax = lo_tax->calculate( '1000' ). WRITE: / 'Dynamic', lv_classname, ':', lv_tax. CATCH cx_sy_create_object_error. WRITE: / 'Class not found:', lv_classname. ENDTRY. * CREATE OBJECT lcl_logger. " syntax error: CREATE PRIVATEReal project scenario
A global rollout added country-specific e-invoicing: India, Germany and Mexico each needed different XML formats. The first version had IF country = … branches in the output program, and every new country meant changes and regression tests in shared code.
The team introduced ZIF_EINVOICE_BUILDER, one class per country and a factory reading a customizing table (country → class). Adding Italy later required one new class and one table entry transported separately — the main program was not touched.
Common mistakes
- CREATE OBJECT of concrete classes scattered in callers.
- Factories returning concrete types instead of interfaces.
- Singletons for data that should be per document.
- Dynamic class names without catching CX_SY_CREATE_OBJECT_ERROR.
- Business logic inside the factory.
Best practices
- Return interface references from factories.
- Keep the factory as the only place that knows implementations.
- Use customizing tables for extensible selection.
- CREATE PRIVATE for controlled instantiation.
- Use singletons sparingly and allow resetting them for tests.
Interview angle
Questions: "What is a factory method and why use it?", "How do you implement a singleton in ABAP?", "Examples of SAP factories?"
Strong answer: a static method that creates and returns an interface-typed object, hiding the concrete class; enables adding implementations without changing callers, often driven by customizing with dynamic CREATE OBJECT TYPE (name); singleton = CREATE PRIVATE, private static instance, get_instance; SAP examples: CL_SALV_TABLE=>FACTORY, GET BADI, CL_BCS=>CREATE_PERSISTENT.
Practice this lesson
Quick check
1. What does CREATE OBJECT ro_tax TYPE zcl_tax_in do when ro_tax is TYPE REF TO zif_tax_calculator?
2. Which statement about interfaces in ABAP is TRUE?
3. Interfaces in ABAP support:
Interview questions
1. Explain RTTI (Runtime Type Information) and RTTS (Runtime Type Services) in ABAP. Design a generic data validation and serialization class using RTTI.
Answer it in your own words first, then compare.
2. Country tax rules are implemented as a long CASE statement in several programs. How would you introduce a factory, and how would you make it extensible without code changes?
Answer it in your own words first, then compare.
Reference-verified: the steps are checked against official SAP documentation and tutorials, and object and field names in the sample code are checked for consistency. The code has not been compiled or run on an SAP system by ERPClimb. Syntax marked release-dependent varies by SAP release, so check it in your own system.
More lessons in this topic
- OOP ABAP Part 1: Classes, Objects, Attributes and Methods
- OOP ABAP Part 10: OOP in S/4HANA, RAP and Clean Core
- OOP ABAP Part 2: Local Classes vs Global Classes
- OOP ABAP Part 3: Inheritance, Interfaces and Polymorphism
- OOP ABAP Part 4: Class-Based Exceptions
- OOP ABAP Part 5: Instance Methods, Static Methods and Constructors
- OOP ABAP Part 6: Encapsulation and Visibility — PUBLIC, PROTECTED, PRIVATE
- OOP ABAP Part 7: Composition vs Inheritance