|
Jun
04
|
Da ich mich seit kurzem intensiv mit Entwurfsmustern auseinander setze, möchte ich jetzt eine Beispielimplementation für das Observer Pattern (Beobachter Muster) in PHP vorstellen.
Hierfür nehmen wir an, wir haben eine Datenquelle, die ein Array mit Punkteständen aus einem beliebigen Spiel übergeben bekommt. Wir möchten diese nun unabhängig von einander darstellen: Durchschnittliche Punktzahl, Summe aller Punkte. Wenn sich die Daten im Objekt der Spielstände ändern, sollen die Darstellungsobjekte automatisch benachrichtigt werden und Daten abrufbereit zur Verfügung stehen.
Schauen wir uns zunächst das UML-Klassendiagramm an, bevor wir uns an den Quellcode machen:

Unser Object “Statistics” enthält als 1-n “Objekte” von Typ “Average” und “Sum”. Diese werden zu Beginn registriert und werden ab sofort bei jeder Änderung der Daten in “Statistics” benachrichtigt. Wir können noch beliebig viele andere Observer-Klassen implementieren, z.B. zum Ausgeben von Diagrammen etc. Dies erfordert nur eine zusätzliche Implementation des Observers.
Legen wir zunächst die Interfaces für unsere Klassen an. Damit stellen wir sicher, dass wir die benötigten Schnittstellen bereitstellen, damit das Muster korrekt arbeiten kann:
1 2 3 4 5 6 7 8 9 10 11 12 | interface Subject { public function attach (Observer $Observer); public function detatch (Observer $Observer); public function notify(); } interface Observer { public function update (Subject $Subject); public function printResult (); } |
Gehen wir kurz die Methoden einzeln durch:
Subject::attach
… bietet die Möglichkeit Observer für die Datenquelle zu registrieren.
Subject::detach
… bietet die Möglichkeit Observer von der Datenquelle zu entfernen.
Subject::notify
… benachrichtigt die registrierten Observer über Änderungen auf der Datenquelle.
Observer::update
… aktualisiert die Daten des Observers bei jedem Update über die Datenquelle
Observer::printResult
… gehört nicht zur Originalimplementation des Observerpatterns, hier führen wir sie trotzdem auf, da wir von jedem Observer erwarten, dass er seine Ergebnisse ausgeben kann.
Schauen wir uns nun die Implementation des Subjects an. Die Methoden sollten eigentlich für sich sprechen. Die Methode setData() wird verwendet, um Daten des Subjects zu ändern. Im Konstruktor wird der initale Datenbestand übergeben. Dieser wird beim Registrieren eines jeden Observers auf dem Subject an den Observer übergeben:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | class Statistics implements Subject { protected $Data = array(); protected $Observers = array(); public function __construct ( $Data ) { $this->setData( (array)$Data ); } public function attach (Observer $Observer) { $Observer->update ( $this ); $this->Observers[] = $Observer; } public function detatch (Observer $Observer) { $this->Observers = array_diff($this->Observers, array($Observer)); } public function notify() { foreach($this->Observers as $Observer) { $Observer->update ( $this ); } } public function setData ( $Data ) { $this->Data = (array)$Data; $this->notify(); } public function getData() { return $this->Data; } } |
Nachdem wir nun das Subject implementiert haben, müssen wir die jeweiligen Darstellungsformen implementieren. Die update-Methode ist, wie gesagt, dazu bestimmt die Daten des Subjects aufzunehmen und aufzubereiten. Ich denke der Rest muss nicht weiter kommentiert werden
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | class Sum implements Observer { protected $Data = array(); public function update(Subject $Statistics) { $this->Data = $Statistics->getData(); } public function printResult() { echo __CLASS__ . ": " . sprintf( "%01.2f" , array_sum ($this->Data) ) . "<br />"; } } class Average implements Observer { protected $Data = array(); public function update(Subject $Statistics) { $this->Data = $Statistics->getData(); } public function printResult() { echo __CLASS__ . ": " . sprintf( "%01.2f" , (array_sum ($this->Data) / count($this->Data) ) ) . "<br />"; } } |
Nachdem wir auch dies erledigt haben, bauen wir ein kleines Testscript, welches die Funktionalitäten testet. Wir übergeben zunächst ein Array mit Daten an das Subject, registrieren zwei Observer und lassen die Daten ausgeben. Anschließend ändern wir die Daten auf dem Subject und lassen die Daten der Observer erneut ausgeben:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | $Data = array ( 123.4, 982.2, 329,1 ); $Data2 = array ( $Data[1], $Data[2] ); $StatisticData = new Statistics ( $Data ); $SumPager = new Sum(); $AveragePager = new Average(); $StatisticData->attach ( $SumPager ); $StatisticData->attach ( $AveragePager ); $SumPager->printResult(); $AveragePager->printResult(); echo "---------Nach Datenaenderung---------<br />"; $StatisticData->setData ( $Data2 ); $SumPager->printResult(); $AveragePager->printResult(); |
Folgende Ausgabe wird nun im Browser erzeugt, wie wir sehen, hat alles gut funktioniert und die Daten sind erfolgreich geändert worden:
Sum: 1435.60
Average: 358.90
———Nach Datenaenderung———
Sum: 1311.20
Average: 655.60
Dies war also ein kleiner Chrashkurs für das Observer-Pattern in PHP 5. Bei Fragen, Anregungen etc. einfach einen Kommentar hinterlassen
.
Kommentare