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
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, thestock
object is thesubject
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.
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.