Bei der Pro­gram­mie­rung von Software gilt es, diverse Aspekte zu beachten: Das End­pro­dukt soll nicht nur über die ge­wünsch­ten Funk­tio­nen, sondern auch über einen möglichst gut lesbaren und einfach nach­zu­voll­zie­hen­den Quellcode verfügen. Der hierfür an­fal­len­de Aufwand soll dabei so gering wie möglich ausfallen – ins­be­son­de­re, wenn Programme oder Pro­gramm­tei­le mit immer wie­der­keh­ren­den Funk­tio­nen oder Elementen entworfen werden. Mit den so­ge­nann­ten GoF-Patterns („Gang of Four“) bzw. -Mustern existiert zu diesem Zweck eine Reihe vor­de­fi­nier­ter Mus­ter­lö­sun­gen für diverse Pro­blem­stel­lun­gen der Software-Ge­stal­tung.

Neben anderen bekannten Patterns wie dem Visitor Pattern oder dem Singleton Pattern zählt auch das so­ge­nann­te Observer Pattern zu dieser Sammlung aus prak­ti­schen De­sign­mus­tern, deren Beachtung den Pro­gram­mier­all­tag erheblich ver­ein­facht. Wir verraten, was es mit dem Ober­ser­ver Design Pattern auf sich hat (inklusive gra­fi­scher Dar­stel­lung in UML) und welche Stärken und Schwächen das Muster hat.

Was ist das Observer Pattern (Be­ob­ach­ter-Ent­wurfs­mus­ter)?

Das Observer Design Pattern, kurz Observer Pattern bzw. deutsch Be­ob­ach­ter-Ent­wurfs­mus­ter, ist eine der be­lieb­tes­ten Mus­ter­vor­la­gen für das Design von Com­pu­ter­soft­ware. Es gibt eine ein­heit­li­che Mög­lich­keit an die Hand, eine Eins-zu-eins-Ab­hän­gig­keit zwischen zwei oder mehreren Objekten zu de­fi­nie­ren, um sämtliche Än­de­run­gen an einem be­stimm­ten Objekt auf möglichst un­kom­pli­zier­te und schnelle Weise zu über­mit­teln. Zu diesem Zweck können sich beliebige Objekte, die in diesem Fall als Observer bzw. Be­ob­ach­ter fungieren, bei einem anderen Objekt re­gis­trie­ren. Letzteres Objekt, das man in diesem Fall auch als Subjekt be­zeich­net, in­for­miert die re­gis­trier­ten Be­ob­ach­ter, sobald es sich verändert bzw. angepasst wird.

Das Observer Pattern zählt, wie eingangs bereits erwähnt, zu den 1994 in „Design Patterns: Elements of Reusable Object-Oriented Software“ ver­öf­fent­lich­ten GoF-Mustern. Die über 20 be­schrie­be­nen Mus­ter­lö­sun­gen für das Soft­ware­de­sign spielen bis heute eine wichtige Rolle in der Kon­zep­tio­nie­rung und Aus­ar­bei­tung von Com­pu­ter­an­wen­dun­gen.

Zweck und Funk­ti­ons­wei­se des Observer Patterns

Das Be­ob­ach­ter-Ent­wurfs­mus­ter arbeitet mit zwei Typen von Akteuren: Auf der einen Seite steht das Subjekt, also das Objekt, dessen Status lang­fris­tig unter Be­ob­ach­tung stehen soll. Auf der anderen Seite stehen die be­ob­ach­ten­den Objekte (Observer bzw. Be­ob­ach­ter), die über sämtliche Än­de­run­gen des Subjekts in Kenntnis gesetzt werden wollen.

Fakt

Ty­pi­scher­wei­se werden einem Subjekt gleich mehrere Observer zu­ge­ord­net. Prin­zi­pi­ell kann das Observer Pattern aber auch nur für ein einziges be­ob­ach­ten­des Objekt an­ge­wen­det werden.

Ohne den Einsatz des Be­ob­ach­ter-Patterns müssten die be­ob­ach­ten­den Objekte das Subjekt in re­gel­mä­ßi­gen In­ter­val­len um Status-Updates bitten – jede einzelne Anfrage wäre mit ent­spre­chen­der Re­chen­zeit inklusive der hierfür er­for­der­li­chen Hardware-Res­sour­cen verbunden. Die grund­le­gen­de Idee des Observer Patterns ist es, die Aufgabe des In­for­mie­rens im Subjekt zu zen­tra­li­sie­ren. Zu diesem Zweck führt es eine Liste, in die sich die Be­ob­ach­ter eintragen können. Bei einer Änderung in­for­miert das Subjekt die re­gis­trier­ten Observer der Reihe nach, ohne dass diese selbst aktiv werden müssen. Ist ein au­to­ma­ti­sches Status-Update nicht mehr für ein be­stimm­tes, be­ob­ach­ten­des Objekt gewünscht, trägt man es einfach wieder aus der Liste aus.

Hinweis

Zur In­for­ma­ti­on der einzelnen Be­ob­ach­ter sind zwei ver­schie­de­ne Methoden möglich: Bei der Push-Methode über­mit­telt das Subjekt den ge­än­der­ten Zustand bereits mit der Be­nach­rich­ti­gung. Das kann jedoch zu Problemen führen, wenn In­for­ma­tio­nen über­mit­telt werden, die der Observer nicht verwerten kann. Dieses Problem besteht bei der al­ter­na­ti­ven Pull-Methode nicht: Bei diesem Ansatz gibt das Subjekt lediglich die In­for­ma­ti­on weiter, dass Än­de­run­gen vor­ge­nom­men wurden. Die Be­ob­ach­ter müssen den ge­än­der­ten Zustand an­schlie­ßend per ge­son­der­tem Me­tho­den­auf­ruf erfragen.

Grafische Dar­stel­lung des Observer Patterns (UML-Diagramm)

Die Funk­ti­ons­wei­se und der Nutzen von Design Patterns wie dem Observer Pattern sind für Au­ßen­ste­hen­de häufig nur schwer zu verstehen. Für ein leich­te­res Ver­ständ­nis bietet sich daher eine grafische Dar­stel­lung der Ent­wurfs­mus­ter an. Ins­be­son­de­re die weit ver­brei­te­te Mo­del­lie­rungs­spra­che UML (Unified Modeling Language) eignet sich hierfür, da sie die Zu­sam­men­hän­ge glei­cher­ma­ßen für Anwender und An­wen­dungs­exper­ten an­schau­lich und greifbar macht. Aus diesem Grund haben wir auch für die nach­fol­gen­de, abstrakte Dar­stel­lung des Observer Patterns auf UML als Dar­stel­lungs­spra­che zu­rück­ge­grif­fen.

Welche Vor- und Nachteile hat das Observer Design Pattern?

Das Observer Pattern in der Software-Ent­wick­lung ein­zu­set­zen, kann sich in vielen Si­tua­tio­nen auszahlen. Der große Vorteil, den das Konzept bietet, ist dabei der hohe Un­ab­hän­gig­keits­grad zwischen einem be­ob­ach­te­ten Objekt (Subjekt) und den be­ob­ach­ten­den Objekten, die sich an dem aktuellen Zustand dieses Objekts ori­en­tie­ren. Das be­ob­ach­te­te Objekt muss bei­spiels­wei­se keinerlei In­for­ma­tio­nen über seine Be­ob­ach­ter besitzen, da die In­ter­ak­ti­on un­ab­hän­gig über die Observer-Schnitt­stel­le erledigt wird. Die be­ob­ach­ten­den Objekte erhalten die Updates derweil au­to­ma­tisch, wodurch er­geb­nis­lo­se Anfragen im Observer-Pattern-System (weil sich das Subjekt nicht geändert hat) gänzlich wegfallen.

Dass das Subjekt alle re­gis­trier­ten Be­ob­ach­ter au­to­ma­tisch über jegliche Än­de­run­gen in­for­miert, ist jedoch nicht immer von Vorteil: Die Än­de­rungs­in­for­ma­tio­nen werden nämlich auch dann über­mit­telt, wenn sie für einen der Observer ir­rele­vant sein sollten. Das wirkt sich ins­be­son­de­re dann negativ aus, wenn die Zahl an re­gis­trier­ten Be­ob­ach­tern sehr groß ist, da an dieser Stelle durch das Observer-Schema eine Menge Re­chen­zeit ver­schenkt werden kann. Ein weiteres Problem des Be­ob­ach­ter-Ent­wurfs­mus­ters: Häufig ist im Quellcode des Subjekts nicht er­sicht­lich, welche Be­ob­ach­ter mit In­for­ma­tio­nen versorgt werden.

Observer Pattern: Wo wird es ein­ge­setzt?

Das Observer Design Pattern ist ins­be­son­de­re in An­wen­dun­gen gefragt, die auf Kom­po­nen­ten basieren, deren Status

  • ei­ner­seits stark von anderen Kom­po­nen­ten be­ob­ach­tet wird,
  • an­de­rer­seits re­gel­mä­ßi­gen Än­de­run­gen un­ter­liegt.

Zu den typischen An­wen­dungs­fäl­len zählen GUIs (Graphical User In­ter­faces), die Nutzern als einfach zu be­die­nen­de Schnitt­stel­le für die Kom­mu­ni­ka­ti­on mit einer Software dienen. Sobald sie Daten verändern, müssen diese in allen GUI-Kom­po­nen­ten ak­tua­li­siert werden – ein Szenario, das optimal durch die Subjekt-Be­ob­ach­ter-Struktur des Observer Patterns abgedeckt wird. Auch Programme, die mit zu vi­sua­li­sie­ren­den Da­ten­sät­zen (ob klas­si­schen Tabellen oder gra­fi­schen Dia­gram­men) arbeiten, pro­fi­tie­ren von der Ordnung durch das Ent­wurfs­mus­ter.

Hin­sicht­lich der ver­wen­de­ten Pro­gram­mier­spra­che gibt es prin­zi­pi­ell keine spe­zi­fi­schen Ein­schrän­kun­gen für das Observer Design Pattern. Wichtig ist lediglich, dass das ob­jekt­ori­en­tier­te Paradigma un­ter­stützt wird, damit eine Im­ple­men­tie­rung des Musters auch sinnvoll ist. Sprachen, in denen der Einsatz des Patterns sehr beliebt ist, sind u. a. C#, C++, Java, Ja­va­Script, Python und PHP.

Observer Pattern: Beispiel für den Einsatz des Be­ob­ach­ter-Ent­wurfs­mus­ters

Wie genau das Observer Design Pattern in den ver­schie­de­nen Pro­gram­mier­spra­chen im­ple­men­tiert wird, un­ter­schei­det sich teilweise sehr stark. Der Grund­ge­dan­ke bleibt jedoch immer der gleiche: Ein be­stimm­tes Objekt bzw. dessen Zustand wird für eine Vielzahl anderer Objekte leichter zu­gäng­lich gemacht. Ein pra­xis­na­hes Beispiel liefert das Observer-Pattern-Tutorial auf ja­v­a­beg­in­ners.de, an dem wir uns an dieser Stelle ori­en­tie­ren möchten.

In dem Beispiel soll ein vom „Erzähler“ ver­öf­fent­lich­ter Text in den Text­fel­dern mehrerer „Zuhörer“ angezeigt werden. Die Klasse Erzaehler (das Subjekt) erweitert zu diesem Zweck die Klasse Ob­ser­va­ble um die Methode ad­dOb­ser­ver(). Dies er­mög­licht das Hin­zu­fü­gen von Zuhoerern (die Observer). Zudem wird die Methode set­Ch­an­ged() ein­ge­führt, die Ver­än­de­run­gen am Subjekt re­gis­triert und im Falle von Neue­run­gen no­ti­f­yOb­ser­vers() aufruft, um alle Be­ob­ach­ter zu in­for­mie­ren.

class Erzaehler extends Observable {
	public Erzaehler(){
		this.addObserver(new Zuhoerer_1());
		this.addObserver(new Zuhoerer_2());
		tell("Text");
	}
	public void tell(String info){
		if(countObservers()>0){
			setChanged();
			notifyObservers(info);
		}
	}
}

Die Observer benötigen zudem eine Im­ple­men­tie­rung der Be­ob­ach­ter-Schnitt­stel­le inklusive der Methode udpate() und zwei Ar­gu­men­ten: dem be­ob­ach­te­ten Objekt und der Änderung in Form einer Objekt-Instanz (das Con­cre­te­Sub­jekt).

class Zuhoerer extends JFrame implements Observer{
	private JTextField field;
	public Zuhoerer(){
		field1 = new JTextField("a");
		add(field);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize(300, 50);
		setVisible(true);
	}
	public void update(Observable o, Object arg) {
		field.setText((String) arg);
	}
}
Zum Hauptmenü