OOP ABAP
ABAP Developmentadvanced

OOP ABAP Part 7: Composition vs Inheritance

Key takeaway

Decide between "is-a" (inheritance) and "has-a" (composition): the fragile base class problem, delegating to helper objects, combining interfaces with composition, and refactoring a deep hierarchy into composed parts.

Explanation

📌 KEY POINTS
✅ Inheritance models "is-a" (a business ticket IS a ticket); composition models "has-a" (a booking HAS a pricing strategy).

✅ Composition: an object holds references to helper objects and delegates work to them.

✅ Prefer composition for reuse; use inheritance only for a true, stable is-a relationship.

⚠️ Deep hierarchies make changes to a superclass risky for all subclasses (fragile base class).

━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE

Part 3 showed inheritance. It is powerful but often overused for code reuse. This lesson shows the alternative.

━━━━━━━━━━━━━━━━━━━━
▶ 1. THE PROBLEM WITH INHERITANCE FOR REUSE

A team builds lcl_order_base → lcl_order_with_pricing → lcl_order_with_pricing_and_tax → lcl_export_order. Every new combination (export without tax, domestic with special pricing) needs another subclass. A change in lcl_order_base affects all of them. ABAP allows only one superclass, so combining two capabilities by inheritance is impossible.

━━━━━━━━━━━━━━━━━━━━
▶ 2. COMPOSITION AND DELEGATION

Instead, the order class has attributes:

  • mo_pricing TYPE REF TO lif_pricing
  • mo_tax TYPE REF TO lif_tax

The order calls mo_pricing->calculate( ) and mo_tax->calculate( ). Different behaviour comes from passing different objects, not from different subclasses.

━━━━━━━━━━━━━━━━━━━━
▶ 3. BENEFITS

  • Combine behaviours freely (any pricing with any tax).
  • Replace parts at runtime or in tests (fake tax calculator).
  • Changes stay local to one small class.
  • Each part has a single responsibility and can be tested alone.

━━━━━━━━━━━━━━━━━━━━
▶ 4. WHEN INHERITANCE IS RIGHT

  • True is-a with shared structure and behaviour, stable over time.
  • Framework requirements: exception classes inherit from CX_STATIC_CHECK, some SAP frameworks expect subclassing of an abstract base (e.g. template-method pattern).
  • Abstract class with shared frame and small specific steps.

━━━━━━━━━━━━━━━━━━━━
▶ 5. RULES OF THUMB

  • "Can I say X is a Y in all situations?" If not → composition.
  • Hierarchies deeper than 2–3 levels → reconsider.
  • Mark classes FINAL by default; open them for inheritance consciously.

━━━━━━━━━━━━━━━━━━━━
▶ 6. REFACTORING A HIERARCHY

Identify the varying behaviours → create an interface for each → move each variant into its own class → give the main class references to them → create objects in a factory (Part 8).

━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP

  1. Run the code example: one order class with two different pricing objects.
  2. Add a new tax implementation without changing lcl_order.
  3. Write down what the inheritance version would need.

✅ CHECKPOINT: you prefer composition for reuse and can justify when inheritance is appropriate.

Code example

ABAP Code
REPORT zoop_composition. * ===== Behaviour interfaces =====INTERFACE lif_pricing.  METHODS calculate IMPORTING iv_base TYPE s_price RETURNING VALUE(rv_price) TYPE s_price.ENDINTERFACE. INTERFACE lif_tax.  METHODS calculate IMPORTING iv_net TYPE s_price RETURNING VALUE(rv_tax) TYPE s_price.ENDINTERFACE. * ===== Small, single-purpose implementations =====CLASS lcl_standard_pricing DEFINITION FINAL.  PUBLIC SECTION.    INTERFACES lif_pricing.ENDCLASS.CLASS lcl_standard_pricing IMPLEMENTATION.  METHOD lif_pricing~calculate.    rv_price = iv_base.  ENDMETHOD.ENDCLASS. CLASS lcl_corporate_pricing DEFINITION FINAL.  PUBLIC SECTION.    INTERFACES lif_pricing.ENDCLASS.CLASS lcl_corporate_pricing IMPLEMENTATION.  METHOD lif_pricing~calculate.    rv_price = iv_base * '0.85'.            " 15% corporate discount  ENDMETHOD.ENDCLASS. CLASS lcl_domestic_tax DEFINITION FINAL.  PUBLIC SECTION.    INTERFACES lif_tax.ENDCLASS.CLASS lcl_domestic_tax IMPLEMENTATION.  METHOD lif_tax~calculate.    rv_tax = iv_net * '0.18'.  ENDMETHOD.ENDCLASS. CLASS lcl_export_tax DEFINITION FINAL.  PUBLIC SECTION.    INTERFACES lif_tax.ENDCLASS.CLASS lcl_export_tax IMPLEMENTATION.  METHOD lif_tax~calculate.    rv_tax = 0.                              " export: no tax  ENDMETHOD.ENDCLASS. * ===== The order HAS a pricing and a tax object (composition) =====CLASS lcl_order DEFINITION FINAL.  PUBLIC SECTION.    METHODS: constructor IMPORTING io_pricing TYPE REF TO lif_pricing                                   io_tax     TYPE REF TO lif_tax,             get_total IMPORTING iv_base TYPE s_price RETURNING VALUE(rv_total) TYPE s_price.  PRIVATE SECTION.    DATA: mo_pricing TYPE REF TO lif_pricing,          mo_tax     TYPE REF TO lif_tax.ENDCLASS. CLASS lcl_order IMPLEMENTATION.  METHOD constructor.    mo_pricing = io_pricing.    mo_tax     = io_tax.  ENDMETHOD.  METHOD get_total.    DATA: lv_net TYPE s_price,          lv_tax TYPE s_price.    lv_net = mo_pricing->calculate( iv_base ).     " delegate    lv_tax = mo_tax->calculate( lv_net ).          " delegate    rv_total = lv_net + lv_tax.  ENDMETHOD.ENDCLASS. DATA: lo_std_pricing  TYPE REF TO lcl_standard_pricing,      lo_corp_pricing TYPE REF TO lcl_corporate_pricing,      lo_dom_tax      TYPE REF TO lcl_domestic_tax,      lo_exp_tax      TYPE REF TO lcl_export_tax,      lo_order        TYPE REF TO lcl_order,      lv_total        TYPE s_price. START-OF-SELECTION.  CREATE OBJECT: lo_std_pricing, lo_corp_pricing, lo_dom_tax, lo_exp_tax. * Any combination without new subclasses  CREATE OBJECT lo_order EXPORTING io_pricing = lo_std_pricing io_tax = lo_dom_tax.  lv_total = lo_order->get_total( '1000' ).  WRITE: / 'Standard + domestic tax:', lv_total.   CREATE OBJECT lo_order EXPORTING io_pricing = lo_corp_pricing io_tax = lo_exp_tax.  lv_total = lo_order->get_total( '1000' ).  WRITE: / 'Corporate + export     :', lv_total.

Real project scenario

A freight calculation framework had grown to 14 subclasses (road, road-express, road-hazardous, sea, sea-hazardous …). Adding "rail with hazardous surcharge" required copying code from two branches because ABAP has single inheritance.
The team refactored into one freight class composed of three strategies: transport mode, surcharge rules and currency rounding. The 14 subclasses became 9 small FINAL classes that can be combined freely, and unit tests cover each strategy separately.

Common mistakes

  • Using inheritance just to reuse a few methods.
  • Deep hierarchies where every combination is a new subclass.
  • Subclasses that depend on protected internals of the superclass.
  • Leaving classes open (non-final) without a reason.
  • Composing concrete classes instead of interfaces.

Best practices

  • Prefer composition + interfaces for varying behaviour.
  • Use inheritance for stable is-a relationships and framework base classes.
  • Keep hierarchies shallow.
  • Inject composed objects via constructor.
  • Mark classes FINAL by default.

Interview angle

Questions: "Composition or inheritance — how do you decide?", "What is the fragile base class problem?", "How do you combine behaviours with single inheritance?"
Strong answer: inheritance for true is-a relationships; composition (has-a) with delegation to interface-typed helper objects for varying behaviour; single inheritance and fragile base classes make deep hierarchies risky; composition allows free combination, runtime replacement and test doubles.

Practice this lesson

Quick check

1. Which statement best describes composition?

2. A billing engine needs any combination of 5 providers and 8 countries. Which design scales best?

3. Which keyword prevents further inheritance?

Interview questions

1. When is inheritance the right choice over composition in ABAP?

Answer it in your own words first, then compare.

2. A billing engine created a subclass for every provider and country combination and now has dozens of subclasses. How would you redesign it?

Answer it in your own words first, then compare.

Practice more with Interview AI →

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

Interview questions on this topic

Related SAP topics