Jump to: navigation, search

OTExample Observer/ObserverPattern

Reusable Implementation of the Observer Pattern

  1. package protocol;
  2.  
  3. import java.util.LinkedList;
  4.  
  5.  
  6. /**
  7.  * This team gives a reusable implementation of the Observer Pattern.
  8.  * Only application-specific parts are left abstract.
  9.  */
  10. public abstract team class ObserverPattern {
  11.  
  12.     /**
  13.      * The Subject role of the observer pattern.
  14.      * Abstractness is not strictly needed, but it signals that applications
  15.      * of the pattern should refine this role.
  16.      */
  17.     protected abstract class Subject {
  18.         /** Registry of known Observers: */
  19.         private LinkedList<Observer> observers = new LinkedList<Observer>();
  20.  
  21.         public void addObserver (Observer o) {
  22.             observers.add(o);
  23.         }
  24.         public void removeObserver (Observer o) {
  25.             observers.remove(o);
  26.         }
  27.         public void removeAllObservers() {
  28.             observers.clear();
  29.         }
  30.         /**
  31.          * All edit operations of the concrete Subject should call into this method.
  32.          */
  33.         public void changeOp() {
  34.             for (Observer observer : observers)
  35.                 observer.update(this);
  36.         }
  37.  
  38.         /**
  39.          *  Variant for multiple changes in one method call.
  40.          *  Because we suspect reentrance at the base side, we temporarily deactivate this Team.
  41.          *    (This solves the problem known as "jumping aspects"
  42.          *     where more notifications would be sent than needed).
  43.          *  By declaring the method as "callin" it is designed to be bound using "replace".
  44.          */
  45.         callin void changeOpMany () {
  46.             boolean wasActive = isActive();
  47.             deactivate();        // no notification during the base call.
  48.             base.changeOpMany(); // call original version (requires "callin" modifier).
  49.             if (wasActive)
  50.                 activate();      // restore state
  51.             changeOp();
  52.         }
  53.     }
  54.  
  55.     /**
  56.      * Observer role of the design pattern.
  57.      */
  58.     protected abstract class Observer {
  59.  
  60.         /**
  61.          *  This method needs to be realized to do something usefull
  62.          *  on the actual observer instance.
  63.          */
  64.         abstract void update(Subject s);
  65.  
  66.         /**
  67.          * To be called, when a concrete observer starts to participate in the pattern.
  68.          * @param s the subject to connect to.
  69.          */
  70.         public void start (Subject s) {
  71.             s.addObserver(this);
  72.         }
  73.  
  74.         /**
  75.          * To be called, when a concrete observer stops to participate in the pattern.
  76.          * @param s the subject to disconnect from.
  77.          */
  78.         public void stop (Subject s) {
  79.             s.removeObserver(this);
  80.         }
  81.     }
  82. }

In preparing for binding this pattern to existing application classes it is implemented as an abstract team. It exhibits two kinds of incompleteness:

Missing Implementation

Abstract classes and methods indicate that the pattern does not define the action an Observer performs when notified: method Observer.update(Subject) is abstract, which forces the class Observer to be abstract, too. Specializations will have two options for filling in the required behaviour: by regularly implementing this method, or, by binding it to an existing method using callout (OTJLD §3).

Missing Trigger

Less obvious is the fact that neither of the "change" methods in Subject is ever called. In fact the missing trigger for either or both of these methods is not expected to be a regular method call, but the intention is to use callin-bindings to one or more existing methods (OTJLD §4).

Normally, only changeOp() would be bound, but changeOpMany() is additionally provided for solving a typical issue with unwanted reentrance: if several methods of the Subject are observed and if one of the observed method (repeatedly) invokes another observed method, this could result in a burst of notifications, where a single notification at the end of the outer method call would suffice. Filtering unwanted triggers is achieved by temporarily deactivating the enclosing team (OTJLD §5.2.b). This way while the outer method call is being executed no further callins are triggered. Only at the end of this bulk operation a call to changeOp() triggers the notification once.

Application Independence

By omitting those two issues, action implementation and trigger definition, the ObserverPattern team remains fully generic, suitable for use in any application.

In this example, two specializations exist that bind the pattern to existing classes: ObservingOpen and ObservingClose