Fernwirkungen
Immer mal wieder hat man das Problem, daß an einem Ort sich entscheidet, was an einem anderen Ort passieren muß. Rede ich in Rätseln? Gut, also ein Beispiel. Nehmen wir den SAX-Parser aus meinem letzten Beitrag. Das Starttag mit seinen Attributen bestimmt, was später daraus werden soll. Oft muß aber zuerst alles abgearbeitet, was zwischen Start- und Endetag eingeschlossen ist, ehe am Endetag die beabsichtigte Wirkung auftreten soll. Wie macht man sowas?
Befehlsmuster (Command Pattern)
Wenn man seinen Gamma im Kopf hat, wird man zuerst darauf kommen, eine Befehlsklasse zu definieren. Die Basisklasse hat nur eine Methode die meist einen Namen wie "run" oder "execute" besitzt. Der Empfänger braucht nur diese Methode zu kennen. Für alle unterschiedlichen Aktionen, die ausgeführt werden sollen, muß eine Ableitung dieser Klasse erzeugt werden. Diese wird dann bei der Behandlung des Starttags instantiiert und auf den Stack gelegt, der das Parsen durch den SAX-Parser kontrolliert. Am Endetag wird dann das Befehlsobjekt vom Stack entfernt und die Befehlsmethode aufgerufen.
Alle benötigten Werte müssen in das Befehlsobjekt hineinkopiert werden, so daß sie am Ziel verfügbar sind.
Closure
Den größten Teil des Aufwands kann man der Programmiersprache überlassen, wenn sie Closures unterstützt. Was sind Closures? Closures stammen aus der funktionalen Programmierung, finden sich in vielen Programmiersprachen wie Python, Ruby, C# 3.0 und vielleicht in der nächsten Version von Java.
Hier zunächst ein Python-Beispiel:
>>> def foo(a):
return bar
>>> x = foo(11)
>>> x(12)
23
Was passiert hier? Innerhalb von der Funktion foo wird die Funktion bar definiert und als Ergebnis von foo zurückgegeben. Das Ergebnis kann jetzt selber aufgerufen werden. Das spannende ist der Parameter von foo. Er befindet sich innerhalb des Kontextes, in dem bar definiert wird, gehört aber selber nicht zu bar. Am Ende von foo wird der Kontext vernichtet, in dem a ursprünglich definiert war. Das bedeutet aber nur, daß die Referenz von foo zu a verloren geht. Das in a enthaltene Objekt hat jetzt immer noch eine Referenz zu bar und kann so überleben. Es ist aber nicht mehr direkt erreichbar sondern in bar eingeschlossen. Daher der Name Closure. Die Runtime sorgt dafür, daß die innere Funktion Referenzen zu allen benötigten Objekten in dem Kontext erhält, in dem sie definiert worden ist.
Damit haben wir aber unser Befehlsmuster ohne eine Klassenhierarchie definieren zu müssen und ohne manuell die benötigten Werte kopieren zu müssen.
In unserem Beispiel des SAX-Parsers würden wir also eine Closure auf den Stack legen und bei der Behandlung des Endetags davon entfernen und ausführen:
saxStack.pop()()