Observer Pattern in Javascript

Das Observer Pattern ist ein viel genutztes Muster aus der Gruppe der Verhaltensmuster. Es dient dazu, Änderungen eines Objektes anderen interessierten Objekten zu signalisieren. Der Vorteil liegt dabei in der losen Kopplung der involvierten Objekte (Lies mehr. Observer Pattern).

Die Standard-Implementierung des Observer Pattern bietet Observern die Möglichkeit sich bei einem Observable anzumelden, während jeder Observer eine Notify Methode bereitstellt, die letztlich bei einer Änderung aufgerufen wird. In C++ würde man hierzu eine abstrakte Klasse Observer implementieren, die Notify als rein virtuelle Methode zur Verfügung stellt. In Java liegt die Umsetzung als Interface nahe. In C# dagegen ist das Observer Muster durch das event Keyword sogar Teil des Sprachstandards.
Ein aktuelles Projekt konfrontierte mich mit der Umsetzung des Pattern in Javascript. Als Prototypen-basierte Sprache lässt sich hier statt abstrakte Klasse oder Interface ein Prototyp mit der geforderten Funktionalität erstellen. Eine direkt umgesetzte Variante hiervon ist hier gegeben. Die gezeigte Implementierung ist nicht besonders ausgefallen: Observer werden in einer einfachen Liste gespeichert und können ebenso wieder aus dieser gelöscht werden. Für das zu beobachtende Subjekt gibt es einen Prototypen den eigene Implementierungen nutzen können.

Einige Bibliotheken für Javascript bieten nun jedoch weitere Möglichkeiten, Observer zu implementieren. JQuery liefert mit Callbacks Object eine Datenstruktur, die eine Menge von callback Funktionen aufnimmt und nach Belieben mit der Methode fire() aufruft. Es übernimmt damit die Verwaltung der Observer für den Entwickler. Der Haken hierbei ist jedoch die Performance. Den Overhead, den jQuery zur Verwaltung des Callback-Objekts braucht spürt man deutlich. Ich habe einen bestehenden Test zur Performance des Observer Pattern in Javascript um eine Variante mit Callbacks-Object erweitert. Die Performance bleibt für die jQuery Variante deutlich hinter der direkten obigen Implementierung zurück. Der Grund ist sicherlich in der zusätzlichen Funktionalität zu suchen: So kann bspw. vom Callbacks-Object abgefragt werden, ob alle callbacks min. 1 mal bisher ausgeführt wurden. Das Callbacks-Object kann außerdem gesperrt werden etc. Hier muss man entscheiden ob diese Funktionalität gebraucht wird und man dafür die Einbußen in der Geschwindigkeit akzeptiert.

Ähnlich verhält es sich mit der im ECMA Skript fest verankerten Funktionalität Object.observe(). Sie wird derzeit (Januar 2015) noch nicht in allen Browsern unterstützt. Lediglich Chrome und Opera bieten sie an. Sie bietet eine komfortabel Unterstützung des Oberserver Patterns: Beobachter werden über jegliche Änderungen (add, update, delete) eines Property informiert. Der Wert vor der Änderung (old value) wird ebenfalls geliefert. Man könnte damit two-way databinding implementieren ohne ein zusätzliches Framework nutzen zu müssen. Hinsichtlich der Performance kann auch diese Variante des Observer Pattern nicht mit der Basis-Implementierung mithalten, bietet dafür aber wesentlich mehr Möglichkeiten, die tatsächlich an C# Databinding errinnern.

Eine allgemeine Diskussion zum Observer Muster ist hier gegeben. Dabei wird auch auf problematische Situationen eingegangen wie bspw. das Löschen eines Observers oder Subjektes, was oftmals bei der Implementierung nicht beachtet wird.