ABAP Objects Design Patterns – Decorator

By | October 17, 2011 | ABAP Objects, OO Design Patterns | 11,174 | 2

Decorator pattern gives us flexibility to extend the behavior of certain objects at runtime. This can be seperate than the existing instances of the same class. To be able to achieve Decorator pattern, we need to create certain infrastructure for it.

Design time consideration:

  1. Create a Subclass of the main class, also called a component, as a Decorator class.
  2. In decorator class, create an attribute with TYPE REF TO main class
  3. Create CONSTRUCTOR in the decorator class with importing parameter REF TO Decorator class. Set the attribute from this parameter.
  4. Redefine all other methods in decorator class inherited from Main Class. Just call the same method using the reference of the main class.
  5. Create subclass of decorator when a new behaviour is required. Redefine the methods for which we need different behaviour.

UML

Let’s see the UML the example:

We have a main class OUTPUT. We have base behavior as ALV output class ALVOUTPUT. This behavior will execute all the time. Now, we’ll add the decorator to it as per the design time consideration steps.

Decorator class OPDECORATOR with attribute O_DECORATOR with TYPE REF TO OUTPUT. In the CONSTRUCTOR, we have the importing parameter IO_DECORATOR. This will set up the link between the sub behavior. Method PROCESS_OUTPUT is redefined to call the method from the O_DECORATOR.

We have three different additional behavior – output in PDF, Email and Excel. This is being achieved by subclassing of the decorator and redefining the PROCESS_OUTPUT method. Along with redefintion we’ll also call the SUPER->PROCESS_OUTPUT( ) to call the previous object’s method in the chain. Actual generation of PDF, Email and Excel is out of scope for this article.

Code Lines

Lets see the code:

REPORT znp_dp_decorator.
*&---------------------------------------------------------------------*
*& Purpose: Decorator Design Pattern Demo
*& Author : Naimesh Patel
*&---------------------------------------------------------------------*

* ===
CLASS output DEFINITION ABSTRACT.
  PUBLIC SECTION.
    METHODS:
      process_output ABSTRACT.
ENDCLASS.                    "output DEFINITION

* ====
CLASS alvoutput DEFINITION INHERITING FROM output.
  PUBLIC SECTION.
    METHODS:
      process_output REDEFINITION.
ENDCLASS.                    "alvoutput DEFINITION

*
CLASS alvoutput IMPLEMENTATION.
  METHOD process_output.
    WRITE'Standard ALV output'.
  ENDMETHOD.                    "process_output
ENDCLASS.                    "alvoutput IMPLEMENTATION

* ====
CLASS opdecorator DEFINITION INHERITING FROM output.
  PUBLIC SECTION.
    METHODS:
      constructor
        IMPORTING io_decorator TYPE REF TO output,
      process_output REDEFINITION.
  PRIVATE SECTION.
    DATAo_decorator TYPE REF TO output.
ENDCLASS.                    "opdecorator DEFINITION

*
CLASS opdecorator IMPLEMENTATION.
  METHOD constructor.
    super->constructor).
    me->o_decorator io_decorator.
  ENDMETHOD.                    "constructor
  METHOD process_output.
    CHECK o_decorator IS BOUND.
    o_decorator->process_output).
  ENDMETHOD.                    "process_output
ENDCLASS.                    "opdecorator IMPLEMENTATION

* =====
CLASS op_pdf DEFINITION INHERITING FROM opdecorator.
  PUBLIC SECTION.
    METHODSprocess_output REDEFINITION.
ENDCLASS.                    "op_pdf DEFINITION

*
CLASS op_pdf IMPLEMENTATION.
  METHOD process_output.
    super->process_output).
    WRITE/(10space'Generating PDF'.
  ENDMETHOD.                    "process_output
ENDCLASS.                    "op_pdf IMPLEMENTATION

* ======
CLASS op_xls DEFINITION INHERITING FROM opdecorator.
  PUBLIC SECTION.
    METHODSprocess_output REDEFINITION.
ENDCLASS.                    "op_xls DEFINITION

*
CLASS op_xls IMPLEMENTATION.
  METHOD process_output.
    super->process_output).
    WRITE/(10space'Generating Excel'.
  ENDMETHOD.                    "process_output
ENDCLASS.                    "op_xls IMPLEMENTATION

* =====
CLASS op_email DEFINITION INHERITING FROM opdecorator.
  PUBLIC SECTION.
    METHODSprocess_output REDEFINITION.
ENDCLASS.                    "op_email DEFINITION

*
CLASS op_email  IMPLEMENTATION.
  METHOD process_output.
    super->process_output).
    WRITE/(10space'Sending Email'.
  ENDMETHOD.                    "process_output
ENDCLASS.                    "op_email IMPLEMENTATION

* ====
CLASS op_alv DEFINITION INHERITING FROM opdecorator.
  PUBLIC SECTION.
    METHODSprocess_output REDEFINITION.
ENDCLASS.                    "op_alv DEFINITION

*
CLASS op_alv IMPLEMENTATION.
  METHOD process_output.
    super->process_output).
    WRITE/(10space'Generating ALV'.
  ENDMETHOD.                    "process_output
ENDCLASS.                    "op_alv IMPLEMENTATION

* ====
CLASS mainapp DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS:
      run IMPORTING
        iv_pdf   TYPE flag
        iv_email TYPE flag
        iv_xls   TYPE flag.
ENDCLASS.                    "mainapp DEFINITION

*
CLASS mainapp IMPLEMENTATION.
  METHOD run.
    DATAlo_decorator TYPE REF TO output,
          lo_pre TYPE REF TO output.          " Helper Variable

* .... Setup objects
*   standarad object
    CREATE OBJECT lo_decorator TYPE alvoutput.
    lo_pre lo_decorator.

*   testing Decorator
    IF iv_pdf IS NOT INITIAL.
      CREATE OBJECT lo_decorator
        TYPE
          op_pdf
        EXPORTING
          io_decorator lo_pre.
      lo_pre lo_decorator.
    ENDIF.
    IF iv_email IS NOT INITIAL.
      CREATE OBJECT lo_decorator
        TYPE
          op_email
        EXPORTING
          io_decorator lo_pre.
      lo_pre lo_decorator.
    ENDIF.
    IF iv_xls IS NOT INITIAL.
      CREATE OBJECT lo_decorator
        TYPE
          op_xls
        EXPORTING
          io_decorator lo_pre.
      lo_pre  lo_decorator.
    ENDIF.

    lo_decorator->process_output).

  ENDMETHOD.                    "run
ENDCLASS.                    "mainapp IMPLEMENTATION

PARAMETERSp_pdf AS CHECKBOX,
            p_email AS CHECKBOX,
            p_xls AS CHECKBOX.

START-OF-SELECTION.
  mainapp=>runiv_pdf p_pdf
                iv_email p_email
                iv_xls   p_xls
                 ).

Run-time explanation

At runtime, we instantiate the object and we pass the previous object as reference. Thus we create the chain. Like when we instantiate the object for PDF, we pass the reference of the base behavior ALV. When we instantiate the object for Email, we’ll pass the object of PDF and so on and so forth. The chain of object would be something similar to this.

When we call the method process_ouput( ) of the LO_DECORATOR class as per the example, it would access each of the object in sequence and start processing from the last object in the chain and start processing in reverse as shown:

Like It? Share!!

Don't miss an Update

Get notified of the new post, right into your inbox

Naimesh Patel{272 articles}

I'm SAP ABAP Consultant for more than a decade. I like to experiment with ABAP especially OO. I have been SDN Top Contributor.
Follow :

Explore all of his 272 articles.

Load comments

2 Comments

  • Hi Naimesh!
    A heavy pattern, the decorator…!
    Thanks for your example!! I’m trying to understand the decorator for a long time now. As there are no examples of how to use the decorator in a SAP context, I still did not understand it. Your example brought me a step further on my decorator way!! ;)
    But I still must admit that I do not understand HOW this works…

    I think in your example is a minor error: You do not use the OP_ALV-class although you defined it.

    But what I am really missing: You are not really «decorating»! In fact you use the inheritance to produce different output. Of course you use the decorator technique, but in my opinion it’s no «decorating» because «decorating» means to put “additional things” to an object.

    Therefore I tried to adapt your example and added costs for each output-variant.

    In my example you will get an output like this:

    Standard ALV output 1.000,00
    Generating ALV 100,00
    Generating PDF 10,00
    Sending Email 50,00
    ===========
    Total Cost: 1.160,00

    The costs will be calculated the same way you produce the ouput: At the end with ONE call of CALCULATE_COSTS.
    I think this describes the meaning of a decorator in a more comprehensible way?!

    Again thanks for your example!
    Btw: I bought the book “Head First Design Patterns” http://goo.gl/UIXgG
    Awsome! a really good book for understanding design patterns! It’s not especially for ABAP but in most cases it can be simply adapted. Some examples are not very good, because they use design patterns (that means at least: classes) to get different prices. Things you normally yould realize with table entries…

    Greetings from good old germany
    Tricktresor-Enno


    REPORT znp_dp_decorator.
    *&---------------------------------------------------------------------*
    *& Purpose : Decorator Design Pattern Demo
    *& Author : Naimesh Patel
    *& Co-Author: Enno Wulff (Adding the costs of each output)
    *&---------------------------------------------------------------------*
    * OUTPUT:
    * Standard ALV output 1.000,00
    * Generating ALV 100,00
    * Generating PDF 10,00
    * Sending Email 50,00
    * ===========
    * Total Cost: 1.160,00
    *
    *----------------------------------------------------------------------*

    * ===
    CLASS output DEFINITION ABSTRACT.
    PUBLIC SECTION.
    DATA deco_cost TYPE wertv8.
    DATA cost TYPE wertv8.
    METHODS:
    process_output ABSTRACT,
    calc_cost ABSTRACT RETURNING value(rv_cost) TYPE wertv8.

    ENDCLASS. "output DEFINITION

    * ====
    CLASS alvoutput DEFINITION INHERITING FROM output.
    PUBLIC SECTION.
    METHODS:
    process_output REDEFINITION
    , calc_cost REDEFINITION.
    .

    ENDCLASS. "alvoutput DEFINITION

    *
    CLASS alvoutput IMPLEMENTATION.
    METHOD process_output.
    deco_cost = 1000.
    WRITE: / 'Standard ALV output', 30 deco_cost.
    cost = calc_cost( ).
    ENDMETHOD. "process_output
    METHOD calc_cost.
    rv_cost = deco_cost.
    ENDMETHOD. "calc_cost

    ENDCLASS. "alvoutput IMPLEMENTATION

    * ====
    CLASS opdecorator DEFINITION INHERITING FROM output.
    PUBLIC SECTION.
    METHODS:
    constructor
    IMPORTING io_decorator TYPE REF TO output,
    process_output REDEFINITION,
    calc_cost REDEFINITION.

    PROTECTED SECTION.
    DATA: o_decorator TYPE REF TO output.
    ENDCLASS. "opdecorator DEFINITION

    *
    CLASS opdecorator IMPLEMENTATION.
    METHOD constructor.
    super->constructor( ).
    me->o_decorator = io_decorator.
    ENDMETHOD. "constructor
    METHOD process_output.
    CHECK o_decorator IS BOUND.
    o_decorator->process_output( ).
    ENDMETHOD. "process_output
    METHOD calc_cost.
    rv_cost = o_decorator->cost + deco_cost.
    ENDMETHOD. "calc_cost
    ENDCLASS. "opdecorator IMPLEMENTATION

    * =====
    CLASS op_pdf DEFINITION INHERITING FROM opdecorator.
    PUBLIC SECTION.
    METHODS: process_output REDEFINITION.

    ENDCLASS. "op_pdf DEFINITION

    *
    CLASS op_pdf IMPLEMENTATION.
    METHOD process_output.
    super->process_output( ).
    deco_cost = 10.
    WRITE: /(10) space, 'Generating PDF', 30 deco_cost.
    cost = calc_cost( ).
    ENDMETHOD. "process_output
    ENDCLASS. "op_pdf IMPLEMENTATION

    * ======
    CLASS op_xls DEFINITION INHERITING FROM opdecorator.
    PUBLIC SECTION.
    METHODS: process_output REDEFINITION.
    ENDCLASS. "op_xls DEFINITION

    *
    CLASS op_xls IMPLEMENTATION.
    METHOD process_output.
    super->process_output( ).
    deco_cost = 20.
    WRITE: /(10) space, 'Generating Excel', 30 deco_cost.
    cost = calc_cost( ).
    ENDMETHOD. "process_output

    ENDCLASS. "op_xls IMPLEMENTATION

    * =====
    CLASS op_email DEFINITION INHERITING FROM opdecorator.
    PUBLIC SECTION.
    METHODS: process_output REDEFINITION.
    ENDCLASS. "op_email DEFINITION

    *
    CLASS op_email IMPLEMENTATION.
    METHOD process_output.
    super->process_output( ).
    deco_cost = 50.
    WRITE: /(10) space, 'Sending Email', 30 deco_cost.
    cost = calc_cost( ).

    ENDMETHOD. "process_output

    ENDCLASS. "op_email IMPLEMENTATION

    * ====
    CLASS op_alv DEFINITION INHERITING FROM opdecorator.
    PUBLIC SECTION.
    METHODS: process_output REDEFINITION.
    ENDCLASS. "op_alv DEFINITION

    *
    CLASS op_alv IMPLEMENTATION.
    METHOD process_output.
    super->process_output( ).
    deco_cost = 100.
    WRITE: /(10) space, 'Generating ALV', 30 deco_cost.
    cost = calc_cost( ).
    ENDMETHOD. "process_output

    ENDCLASS. "op_alv IMPLEMENTATION

    * ====
    CLASS mainapp DEFINITION.
    PUBLIC SECTION.
    CLASS-DATA l_cost TYPE wertv8.
    CLASS-METHODS:
    run IMPORTING
    iv_pdf TYPE flag
    iv_email TYPE flag
    iv_xls TYPE flag.
    ENDCLASS. "mainapp DEFINITION

    *
    CLASS mainapp IMPLEMENTATION.
    METHOD run.
    DATA: lo_decorator TYPE REF TO output,
    lo_pre TYPE REF TO output. " Helper Variable

    * .... Setup objects
    * standard object
    CREATE OBJECT lo_decorator TYPE alvoutput.

    *==> Call Main output: ALV
    lo_pre = lo_decorator.
    CREATE OBJECT lo_decorator
    TYPE
    op_alv
    EXPORTING
    io_decorator = lo_pre.

    IF iv_pdf IS NOT INITIAL.
    *==> Decorating: PDF
    lo_pre = lo_decorator.
    CREATE OBJECT lo_decorator
    TYPE
    op_pdf
    EXPORTING
    io_decorator = lo_pre.
    ENDIF.
    IF iv_email IS NOT INITIAL.
    *==> Decorating: PDF
    lo_pre = lo_decorator.
    CREATE OBJECT lo_decorator
    TYPE
    op_email
    EXPORTING
    io_decorator = lo_pre.
    ENDIF.
    IF iv_xls IS NOT INITIAL.
    *==> Decorating: PDF
    lo_pre = lo_decorator.
    CREATE OBJECT lo_decorator
    TYPE
    op_xls
    EXPORTING
    io_decorator = lo_pre.
    lo_pre = lo_decorator.
    ENDIF.

    *== Process output
    lo_decorator->process_output( ).
    *== Calculate costs
    l_cost = lo_decorator->calc_cost( ).
    write: /40 '==========='.
    WRITE: / 'Total Cost:', 30 l_cost.

    ENDMETHOD. "run
    ENDCLASS. "mainapp IMPLEMENTATION

    PARAMETERS: p_pdf AS CHECKBOX DEFAULT 'X',
    p_email AS CHECKBOX DEFAULT 'X',
    p_xls AS CHECKBOX.

    START-OF-SELECTION.
    mainapp=>run( iv_pdf = p_pdf
    iv_email = p_email
    iv_xls = p_xls
    ).

  • Naimesh Patel

    Hello Enno,

    Nice to here from you!

    Thanks for adding additional functionality to the example to make it more clear.

    We already have a base functionality – ALV Output achieved by ALVOUTPUT class. We can add more functionality to the MAINAPP, without touching existing functionality. If you think from the MAINAPP perspective, we have achieved a decorator. Lets say you wish to create another Output of your data, fancy excel using XML. So, what you do is you inherit another class from the OPDECORATOR. In your MAINAPP, you add this new object in the chain to process it. You can add new functionality (decorating) your existing functionality.

    More real business scenario would be any bolt on functionality to an existing program. If the designer had created decorator, it would be easier for the person who is going to modify it. As he needs to inherit a subclass, call the SUPER method in each method and finished.

    If you are getting confused from enhancing the existing functionality of the object (e.g. ALV, or XLS or PDF), I guess it wont be Decorator. What do you think?

    Regards,
    Naimesh Patel

Comments on this Post are now closed. If you have something important to share, you can always contact me.

You seem to be new here. Subscribe to stay connected.