#

OO Observer Pattern

Das Observer Pattern ist wohl eines der einfachsten und am häufigsten benutzten Patterns überhaupt. Um so beschämender ist die Umsetzung dieses Musters in der Java API (java.util.Observable). Wenn man dieses nämlich benutzen möchte, so muss man von Observable erben, welches bei Einfachvererbung dazu führt, dass man von eigenen Klassen nicht mehr erben kann.
Nun habe ich mich nicht umsonst etliche Semester mit OO-Konzepten herumgeschlagen und habe daher eine wiederverwertbare, den OO-Konzepten besser entsprechende Umsetzung des Observermusters erstellt.

Das Observermuster besteht bekanntermaßen aus einem Objekt, das observiert wird (Observable) und einem, welches die Observierung macht (Observer). Dabei muss sich der Observer beim Observable anmelden, auf dass er bei Änderungen im Observable von diesem benachrichtigt wird.

Der erste Schritt besteht daraus, die in der Java API benötigte Vererbung durch Delegation aufzuheben. Jeder Observable erhält einen Observermanager, welcher für die Verwaltung und Benachrichtigung aller beim Observable registrierten Observer übernimmt. Hierzu werden zwei Interfaces definiert: eines für den Observable und eines für den Observermanager.

public interface IObserverManager{
  public void addObserver(IObserver o);
  public void removeObserver(IObserver o);
  public void notifyObservers(IObservable o);
}

Die Methoden sprechen für sich. notifyObservers(…) kann nun vom Observable benutzt werden, um alle Observer zu benachrichtigen. Jeder Observable muss seine eigene Observermanager Instanz benutzen. Das Interface für den Observable sieht entsprechend so aus:

public interface IObservable{
  public IObserverManager getObserverManager();
}

Die Registrierung eines Observers bei einem Observable wird also auf die Observermanager deligiert. Schließlich das Interface für die Observer:

public interface IObserver{
  public void notify(IObservable o);
}

Da die Aufgaben eines Observermanagers stets die gleichen sind, lohnt es sich eine einfache Implementierung zu erstellen und wieder zu benutzen. Zur Aufbewahrung der Observer kann jegliche lineare Datenstruktur benutzt werden, wie z.B. alle Klassen, die das Interface java.util.List implementieren. Im folgenden benutze ich eine ArrayList.

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
 
public class ObserverManager implements 
  IObserverManager{
  private List mObservers = new ArrayList();
 
  public void addObserver(IObserver o){
    if (!mObservers.contains(o)) mObservers.add(o);
  }
 
  public void removeObserver(IObserver o){
    mObservers.remove(o);
  }
 
  public void notifyObservers(IObservable o){
    Iterator iter = mObservers.iterator();
    while (iter.hasNext())
       ((IObserver) iter.next()).notify(o);
  } 
}

Der Einsatz ist denkbar einfach und kommt ganz ohne Vererbung aus. Das Observable muss lediglich eine Instanz vom Observermanager bereitstellen und bei bedarf benutzen:

public class MyObservabe implements IObservable{
  private IObserverManager mObserverManager = 
    new ObserverManager();
  /* ... */
  public void changeState(){
    /* ... */
    mObserverManager.notifyObservers(this);
  }
 
  public IObserverManager getObserverManger(){
    return mObserverManager;
  }
}

Die Observer müssen sich lediglich registrieren, oder registriert werden und die Methode notify(IObservable) implementieren:

public class MyObserver implements IObserver{
  private MyObservable myObservable = 
    new MyObservable();
 
  public MyObserver(){
    myObseravble.getObserverManager().
       addObserver(this);
  }
 
  public void notify(IObservable o){
    if (myObservable==o){
      myObservable.getState();
      doSomething();
    }
  }
}

Die if-Bedingung zur Identifizierung des Observables kann natürlich nur benutzt werden, wenn der Observable bekannt ist. Wurde der Observable aussen Erzeugt, so muss man auf instanceof zurück greifen. Eine Verknüpfung zwischen Observer und Observable durch eine weitere Klasse, z.B. im Rahmen eines Factory oder Builder Patterns kann wie folgt geschehen:

public class Main{
  public static void main(String [] argv){
    IObservable myObservable = new MyObservable();
    IObserver myObserver = new MyObserver();
    myObservable.getObserverManager().
       addObserver(myObserver);
  }
}

Wird im Observable die notifyObservers(…)-Methode des Observermanagers sehr häufig benutzt, kann durch eine Änderung des Interface IObserverManager der aufruf verkürzt werden:

public interface IObserverManager{
  public void addObserver(IObserver o);
  public void removeObserver(IObserver o);
  public void notifyObservers();
}

Hier wird das Benachrichtigen der Observer etwas stärker gekapselt: In der vorherigen Version hätte ein anderer Observable durch folgenden Code die Observer eines anderen Observable aufrufen können:

public class MySecondObservable implements IObservable{
  private IObservable myOtherObservable = 
     new MyObserable();
  /* ... */
  public void exploitOtherObservable(){
    myOtherObservable.getObserverManager().
      notifyObservers(this);
  }
}

Nun muss jedoch dem Observermanager bei der Erzeugung sein Observable Objekt mitgeteilt werden:

public class ObserverManager implements IObserverManager{
  private IObservable mObservable;
 
  public ObserverManager(IObservable o){
    mObservable = o;
  }
 
  /* ... */
 
  public void notifyObservers(){
    Iterator iter = mObservers.iterator();
    while (iter.hasNext())
       ((IObserver) iter.next()).notify(mObservable);
  }
 
  /* ... */
}
Tags:,

2 Responses to “OO Observer Pattern” »»

  1. Comment by SICk-LR | 10:47 12.07.07|X

    Ein sehr schön geschriebene Beitrag, jedoch fehlt mir ein wenig der Bezug zur Theoretischen Informatik, das das Subjekt(hier: Observerable) die Eventquelle ist, und der hier Observer der Listener ist.

  2. Comment by Secco | 14:36 12.07.07|X

    Danke für das Kompliment. Tatsächlich ging es mir in diesem Beitrag primär um die Umsetzung des Musters, bei der die Kenntnis des Musters vorausgesetzt wird.

Leave a Reply »»

Note: All comments are manually approved to avoid spam. So if your comment doesn't appear immediately, that's ok. Have patience, it can take some days until I have the time to approve my comments.