ABAP Observer Design Pattern

Summary: this tutorial discusses the Observer design pattern and how it is implemented in ABAP.

Problem

It is necessary to maintain the consistency between related objects without making classes tightly coupled.

Suppose that you have to develop a simple stock application that tracks prices throughout the day on SAP. The stock prices have to be displayed on a dashboard or sent via email or SMS to investors if they are lower or higher at certain prices.

Whenever the stock prices are changed, the dashboard needs to reflect that changes and investors may get the SMS or email notification based on the price.

To develop this application, you need the observer design pattern so whenever the state of on object changes (stock’s price) the related objects ( dashboard, investors…) get notified.

Intent

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Observer Design Pattern UML Diagram

ABAP Observer Design Pattern UML Diagram
ABAP Observer Design Pattern UML Diagram

There are two actors in the observer design pattern:

  • Subject. The subject object keeps track of its observers. In addition, it provides an interface for register / unregister observer objects. Whenever the state of subject-object changes, it notifies all of its observers immediately. In our example above, the stock object is the subject object.
  • Observer: The observer object defines an interface for updating notification.
  • ConcreteObserver:  We can have multiple observer objects that inherit from the observer class or implements the observer interface. In our example above, the dashboard and investors are the Observer or ConcreteObserver objects.

ABAP Observer Design Pattern Implementation

Let’s implement Observer design pattern in ABAP.

Here is the UML diagram of ABAP observer design pattern that we will implement in the section below.

ABAP Observer Design Pattern
ABAP Observer Design Pattern

First, define an observer interface that provides a method for updating notification:

INTERFACE lif_observable.
  METHODS:
    register IMPORTING im_observer TYPE REF TO if_observer,
    unregister IMPORTING im_observer TYPE REF TO if_observer.
ENDINTERFACE.                    "lif_observable

The if_observer interface contains only one method called notify that accepts any object as a parameter.

Second, define the subject interface which we call observable. The observable interface provides methods for managing the observer objects.

INTERFACE lif_observable.
  METHODS:
    register IMPORTING im_observer TYPE REF TO if_observer,
    unregister IMPORTING im_observer TYPE REF TO if_observer.
ENDINTERFACE.                    "lif_observable

Third, define a base class for all observable objects that implement the observable interface. A list of observer objects is stored in an internal table.

*----------------------------------------------------------------------*
* Observable definition which implements the observable interface
*----------------------------------------------------------------------*
CLASS cl_observable DEFINITION.
  PUBLIC SECTION.
    INTERFACES: lif_observable.
    ALIASES: register FOR   lif_observable~register,
             unregister FOR lif_observable~unregister.
    METHODS: notifyobservers.

  PRIVATE SECTION.
    DATA: mt_observer TYPE STANDARD TABLE OF REF TO if_observer.
ENDCLASS.                    "cl_observable DEFINITION

*----------------------------------------------------------------------*
*       CLASS cl_observable IMPLEMENTATION
*----------------------------------------------------------------------*
* Observable implementation
*----------------------------------------------------------------------*
CLASS cl_observable IMPLEMENTATION.
  METHOD register.
    APPEND im_observer TO mt_observer.
  ENDMETHOD.                    "register

  METHOD unregister.
    DELETE TABLE mt_observer FROM im_observer.
  ENDMETHOD.                    "unregister

  METHOD notifyobservers.
    FIELD-SYMBOLS <observer> TYPE REF TO if_observer.
    LOOP AT mt_observer ASSIGNING <observer>.
      <observer>->notify( me ).
    ENDLOOP.
  ENDMETHOD.                    "notifyObservers
ENDCLASS.                    "cl_observable IMPLEMENTATION

Fourth, we define the stock class that inherits from the observable class. Whenever the price of the stock changes, we notify all of its observers.

*----------------------------------------------------------------------*
*       CLASS cl_stock DEFINITION
*----------------------------------------------------------------------*
*  concrete observable definition
*----------------------------------------------------------------------*
CLASS cl_stock DEFINITION INHERITING FROM cl_observable.
  PUBLIC SECTION.
    METHODS:
      constructor IMPORTING im_name TYPE string
                            im_price TYPE p,

      set_price IMPORTING im_price TYPE price_typ,
      get_price RETURNING value(re_price) TYPE price_typ,

      get_name RETURNING value(re_name) TYPE string.
  PRIVATE SECTION.
    DATA: mv_name  TYPE string,
          mv_price TYPE price_typ.
ENDCLASS.                    "cl_stock DEFINITION

*----------------------------------------------------------------------*
*       CLASS cl_stock IMPLEMENTATION
*----------------------------------------------------------------------*
* concrete observable definition implementation
*----------------------------------------------------------------------*
CLASS cl_stock IMPLEMENTATION.
  METHOD constructor.
    super->constructor( ).
    " set name and price
    mv_name  = im_name.
    mv_price = im_price.
  ENDMETHOD.                    "constructor
  METHOD set_price.
    IF im_price <> mv_price.
      mv_price = im_price.
      notifyobservers( ). " notify observer
    ENDIF.
  ENDMETHOD.                    "set_price

  METHOD get_price.
    re_price = mv_price.
  ENDMETHOD.                    "get_price

  METHOD get_name.
    re_name = mv_name.
  ENDMETHOD.                    "get_name
ENDCLASS.                    "cl_stock IMPLEMENTATION

Finally, we define a dashboard class that simply displays the stock information.

*----------------------------------------------------------------------*
*       CLASS cl_dashboard DEFINITION
*----------------------------------------------------------------------*
* Concrete observer definition
*----------------------------------------------------------------------*
CLASS cl_dashboard DEFINITION.
  PUBLIC SECTION.
    INTERFACES if_observer.
    ALIASES: notify FOR if_observer~notify.
ENDCLASS.                    "cl_dashboard DEFINITION

*----------------------------------------------------------------------*
*       CLASS cl_dashboard IMPLEMENTATION
*----------------------------------------------------------------------*
* Concrete observer implementation
*----------------------------------------------------------------------*
CLASS cl_dashboard IMPLEMENTATION.
  METHOD notify.
    DATA: lv_name  TYPE string,
          lv_price TYPE price_typ,
          lo_stock TYPE REF TO cl_stock.

    lo_stock ?= im_param.
    lv_name   =  lo_stock->get_name( ).
    lv_price  =  lo_stock->get_price( ).

    WRITE: / lv_name , 'currently has price $', lv_price.
  ENDMETHOD.                    "notify
ENDCLASS.                    "cl_dashboard IMPLEMENTATION

Now, it is time to test our classes:

DATA: go_display TYPE REF TO cl_dashboard,
      go_stock   TYPE REF TO cl_stock.

START-OF-SELECTION.
  CREATE OBJECT go_stock
    EXPORTING
      im_name  = 'IBM'
      im_price = '169.20'.

  CREATE OBJECT go_display.

  go_stock->register( go_display ).

  go_stock->set_price('169.21').
  go_stock->set_price('161.23').
  go_stock->set_price('160.01').

In this tutorial, you’ve learned how to implement the observer design pattern in ABAP that allows related objects to get notified when the state of an object changes.