Gyan Factory

Gyan Factory
SAP Technical Project Support

Tuesday, February 16, 2016

ABAP Objects Design Patterns – Decorator

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 theCONSTRUCTOR, 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:

No comments:

Post a Comment