Een aspect van het Observer patroon

Ik wil hier graag een aspect van het Observer-patroon bespreken. Het Observer-patroon bespreekt hoe een object kan aangeven dat het veranderd is, zonder harde koppelingen te maken. In het boek “Design Patterns” van de Gang of Four (GoF) wordt een aanvulling besproken. Het behandelt de situatie, dat een Observer alleen geïnteresseerd is in een bepaald aspect van een verandering. In deze post geef ik mijn interpretatie hiervan.

In Domain Driven Design staat het model centraal. Het model mag services er omheen niet kennen. Het model kan services op de hoogte houden van toestandveranderingen door middel van events. Een manier om events te implementeren is door het Observer-patroon to te passen. In het huidige project waaraan ik werk, kwamen we voor deze situatie te staan.

We ondervonden daarbij het volgende probleem: Het kan voorkomen dat een Observer alleen geïnteresseerd is in een bepaald aspect van een toestandsverandering van een object. Stel je een effectenmakelaar voor. Deze makelaar handelt in een aandeel en wil op de hoogte gesteld worden van de waardeverandering. Maar hij is alleen maar geïnteresseerd als het aandeel in waarde daalt. De effectenmakelaar kan dan beslissen het aandeel te verkopen.

Het is niet wenselijk om voor ieder aspect een aparte registreermethode te hebben, ala
...
AddWaardeVerhoogdObserver
AddWaardeVerlaagdObserver
....

Deze methoden zullen veel op elkaar lijken. Het is verstandig het aspect te abstraheren. Dit idee wordt al besproken door de GoF. In de opmerking paragraaf bespreken zij de toevoeging van aspecten. In de rest van deze post bespreek ik mijn interpertaties van deze opmerking.

Aspecten

Hieronder staat het UML diagram van het Observer-patroon met aspecten. Het originele patroon is duidelijk herkenbaar. Eén van de veranderingen is de toevoeging van een Aspect-klasse. Hierdoor is het voor een Observer mogelijk te concentreren op een interessante verandering. Mijn interpertatie van aspecten is als volgt.

Een aspect komt zelf voor binnen een hiërarchie van aspecten. Sommige aspecten zijn specifieker dan andere. Denk nog even terug aan onze effectenmakelaar. De aspecten aan een aandeel zijn bijvoorbeeld waarde veranderd, waarde gestegen en waarde gedaald. De laatste twee zijn specifiekere veranderingen van het eerste type. Ik heb er dan ook voor gekozen om aspecten als klassen elkaar te laten overerven.

Dit gebruik ik in de notifyObserver(Aspect) methode:

public void notifyObservers(Aspect aspect) {

  if (aspect != null) {

    if (aspectToObservers.containsKey(aspect)) {

      for (Observer observer: aspectToObservers.get(aspect)) {

        observer.update(this, aspect);

      }

    }

    notifyObservers(aspect.getAParent());

  }

}

Hieraan zie je dat niet alleen Observers van het huidige aspect op de hoogte worden gesteld. Ook van het superklasse van het huidige aspect. De methode getAParent() van Aspect staat hieronder. Er wordt met reflectie een instantie van een superklasse gemaakt als deze de Aspect-interface implementeert.

public Aspect getAParent() {

  Aspect parent = null;

  Class baseClass = this.getClass();

  Class superClass = baseClass.getSuperclass();

  if (superClass != null) {

    Class[] superInterfaces = superClass.getInterfaces();

    for (Class superInterface: superInterfaces) {

      if (superInterface.equals(Aspect.class)) {

        try {

          parent = (Aspect) superClass.newInstance();

        } catch (InstantiationException e) {

          assert false;

        } catch (IllegalAccessException e) {

          assert false;

        }

      }

    }

  }

  return parent;

}

Aspecten geven toestandsveranderingen in het model aan. Observers kunnen specifieke aspecten observeren. Het gedrag van een Observer is geabstraheerd in een Handler. Een handler is in de regel zeer object-specifiek. Een specifiek object observeert een ander specifiek object. Ik vind het dan ook gelegitimeerd om in de handlers observer en subject naar hun werkelijke type te casten.
Om de werking van bovenstaande code te illustreren heb ik een voorbeeld geimplementeerd.

Voorbeeld
In dit voorbeeld is er een moeder met kinderen. Kinderen zijn in een bepaalde stemming en hebben een bepaalde afstand tot hun moeder. Kinderen laten hun moeder van hun toestandsveranderingen weten. Hieronder vind je het klasse-diagram van de aspecten. De kinderen laten moeder alleen van de meest specifieke veranderingen weten.

Alles is terug te vinden in mee geleverde jar file. Het kan worden uitgevoerd met observerPatternExample.jar. De source bestanden zijn ook ingepakt. Zo heb ik er voor gekozen om de verantwoordelijkheid van het implementeren van de interfaces te delegeren. Zo hoeft maar een keer een implementatie geschreven te worden.