Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: for the plan.

Jump to: navigation, search

OTExample Observer/ObservingOpen

Application of the Observer pattern

This team binds the ObserverPattern:

A few comments are given inline, for more explanation see below.

  1. package flower_example;
  3. import protocol.ObserverPattern;
  4. /**
  5.  * @author Bruce Eckel (original Java example)
  6.  * @author Miguel P. Monteiro (adaptation to OT/J)
  7.  */
  8. public team class ObservingOpen extends ObserverPattern {
  10.     @Override
  11.     protected class Subject playedBy Flower
  12.     {
  13.         // This method  implicitly uses a private field of its base class: isOpen
  14.         @Override
  15.         @SuppressWarnings({ "inferredcallout", "decapsulation" })
  16.         public void changeOp() {
  17.             // manual implementation, see ObservingClose.Subject for an abbreviation.
  18.             if(!this.isOpen)
  19.                 tsuper.changeOp();
  20.         }
  21.         changeOp <- before open;
  22.     }
  24.     public class BeeObserver extends Observer playedBy Bee {
  25.         update -> haveBreakfast;
  26.     }
  28.     public class HummingbirdObserver extends Observer playedBy Hummingbird {
  29.         update -> breakfastTime;
  30.     }
  32.     // This and the following signature applies OTJLD §2.3.2(e):
  33.     public <AnyBase base Observer>
  34.     void mapSubject2Observer(Flower as Subject subject, AnyBase as Observer observer) {
  35.         subject.addObserver(observer);
  36.     }
  38.     public <AnyBase base Observer>
  39.     void removeObserverFromSubject(Flower as Subject subject, AnyBase as Observer observer) {
  40.         subject.removeObserver(observer);
  41.     }
  43.     public void removeAllObserversFromSubject(Flower as Subject subject) {
  44.         subject.removeAllObservers();
  45.     }
  46. }


Applying the pattern happens in 4+1 steps involving: (1) teams, (2) roles, (3) callout bindings and (4) callin bindings. Additionally registration methods are provided for defining at runtime which instances should participate in this pattern.

Team Specialization

Applying the generic pattern is implemented by sub-classing the team ObserverPattern.

Role Specialization and Binding

The roles of the pattern are specialized using two slightly different mechanisms: roles BeeObserver and HummingbirdObserver use regular inheritance (extends) for specialization and bind using playedBy to one existing base class each.

Role Subject on the other hand uses implicit inheritance, which is a combination of overriding the inherited abstract class Subject and also implicitly inheriting from the class being overridden. Thus implicit inheritance (OTJLD §1.3.1) can be understood as continuing with the inherited definition of class Subject. Similar to the two previous roles also Subject is bound to a base class using playedBy.

Callout bindings

The missing action implementation of both specialized Observer roles is bound using callout (OTJLD §3), as seen in lines 25 and 29. By these bindings the update(Subject) method is no longer abstract.

Callin bindings

Binding a trigger for changeOp() is basically as simple as the above callout bindings. The callin binding in line 21 defines that whenever method open() is called on a Flower, the notification should be triggered before the regular behaviour. However, In order to observe only open() calls that actually have an effect, method changeOp() is refined so that the notification mechanisms will only be called if the Flower was not already open.

Here the expression this.isOpen demonstrates one way how a role can access state of its base: we let the compiler implicitly infer a callout accessor to the given base class field (OTJLD §3.5.h). Callout inference has to be enabled by a compiler preference and if configured as warnings the compiler urges the developer to document the use of inferred callouts by saying @SuppressWarnings("inferredcallout").

Additionally, method changeOp() demonstrates how to invoke the super method in case of implicit inheritance: by using tsuper instead of super we denote that a same-named class from the super-team should be searched for the method (OTJLD §1.3.1.f).

Registration methods

The three methods defined at the team level (lines 32-45) provide a facade for the add/remove methods of class Subject.

Method removeAllObserversFromSubject(Flower as Subject subject) expects a Flower instance as its argument but before entering the method body this instance is lifted to its Subject role (OTJLD §2.3.2). If lifting finds no matching role for this Flower a new role instance is created implicitly.

For the other two methods signatures are a bit more advanced. The intention is to define a method that receives a Flower (as the Subject) and either a Bee or a Hummingbird (as the Observer). Since Bee and Hummingbird have no common supertype (aside from useless Object) the method is declared generically by introducing a type parameter AnyBase. Now a free type parameter would be too weak (just like specifying Object would be). So we constrain the type parameter to only those classes that are a base class of role Observer. This is a precise definition of the set {Bee,Hummingbird} (taking into account that the sub-classes BeeObserver and HummingbirdObserver are conform to the type bound Observer). By declaring <AnyBase base Observer> we say that AnyBase can stand for any type that is liftable to Observer (OTJLD §2.3.2.e). This capability is used by actually requesting this lifting in AnyBase as Observer observer. See the Main class for applications of these methods.

This concludes the application of the ObserverPattern for observers interested in when a Flower opens. Note, how little coding is actually required since most tasks are performed by declarative bindings.

Back to the top