Robert Walter MSI 3 EMFW WS 08/09 Besucher Muster (Visitor) Ein objektbasiertes Verhaltensmuster 15.01.09 Beschreibung I • Klassifizierung: – Objektbasiertes Verhaltensmuster • Zweck: – Kapsle eine auf den Elementen einer Objektstruktur auszuführende Operation als ein Objekt • Motivation: – Übersetzer, der auf Elemente seiner Sprache verschiedene Operationen ausführen muss: • Typisierung, Code‐Generierung, Pretty‐Printing, etc. – Unterschiedliche Elemente verlangen unterschiedliche Implementierungen der Operationen • => Jedes Element (jede Klasse der Baumstruktur) implementiert jede Operation 15.01.2009 Robert Walter ‐ MSI 3 2 / 46 Beschreibung II • Fortsetzung Motivation Knoten +PruefeTypisierung() +GeneriereCode() +PrettyPrint() VariablenRefKnoten ZuweisungsKnoten MethDeklKnoten +PruefeTypisierung() +GeneriereCode() +PrettyPrint() +PruefeTypisierung() +GeneriereCode() +PrettyPrint() +PruefeTypisierung() +GeneriereCode() +PrettyPrint() – Verwirrende Struktur • z.B. Pretty‐Printing & Typisierung vermischt – Hinzufügen von neuen Operationen sehr aufwendig 15.01.2009 Robert Walter ‐ MSI 3 3 / 46 Beschreibung III • Fortsetzung Motivation – Verbesserungen • Operationen sind vom Klassenbaum unabhängig • Knotenklassen unabhängig von den auf sie anzuwendenden Operationen – Realisierung • Zusammenfassen verwandter Operationen jeder Klasse in separatem Objekt => Besucher • Bei Traversierung erhalten Knoten Besucher als Parameter übergeben • Akzeptiert ein Knoten den übergebenen Besucher, ruft er eine entsprechende Bearbeitungs‐Operation des Besuchers auf und übergibt wiederum sich selbst als Parameter 15.01.2009 Robert Walter ‐ MSI 3 4 / 46 Beschreibung IV Besucher +Besuche(ZuweisungsKnoten) +Besuche(VariablenRefKnoten) +Besuche(MethDeklKnoten) TypPruefungsBesucher +Besuche(ZuweisungsKnoten k) +Besuche(VariablenRefKnoten k) +Besuche(MethDeklKnoten k) CodeGenerierungsBesucher +Besuche(ZuweisungsKnoten k) +Besuche(VariablenRefKnoten k) +Besuche(MethDeklKnoten k) Programm PrettyPrintBe... +Besuche(Zuwei... +Besuche(Varia... +Besuche(MethD... Knoten +NimmEntgegen(Besucher) VariablenRefKnoten ZuweisungsKnoten MethDeklKnoten +NimmEntgegen(Besucher b) +NimmEntgegen(Besucher b) +NimmEntgegen(Besucher b) b->Besuche(this); 15.01.2009 Robert Walter ‐ MSI 3 b->Besuche(this); b->Besuche(this); 5 / 46 Anwendbarkeit • Verwenden, wenn – man klassenabhängige Operationen auf Objekte großer Strukturen mit unterschiedlicher Schnittstelle ausführen will – man unterschiedliche Operationen auf viele Objekte von den Objekten trennen will, um „Objektverschmutzung“ zu vermeiden; Operations‐Kapselung ermöglicht Verteilung der Aufgaben auf mehrere Anwendungen – die Klassen der Struktur sich nur selten ändern, die darauf ausgeführten Operationen aber oft; Strukturänderungen erfordern eine Neudefinition der Besucherschnittstellen! 15.01.2009 Robert Walter ‐ MSI 3 6 / 46 Teilnehmer I Klient Besucher +Besuche(KonkretesElementA) +Besuche(KonkretesElementB) KonkreterBesucher2 +Besuche(KonkretesElementA) +Besuche(KonkretesElementB) ObjektStruktur KonkreterBesucher2 +Besuche(KonkretesElementA) +Besuche(KonkretesElementB) Element +NimmEntgegen(Besucher) KonkretesElementA KonkretesElementB +NimmEntgegen(Besucher b) +OperationA() +NimmEntgegen(Besucher b) +OperationB() b->Besuche(this); 15.01.2009 Robert Walter ‐ MSI 3 b->Besuche(this); 7 / 46 Teilnehmer II Besucher +Besuche(KonkretesElementA) +Besuche(KonkretesElementB) KonkreterBesucher1 +Besuche(KonkretesElementA) +Besuche(KonkretesElementB) KonkreterBesucher2 +Besuche(KonkretesElementA) +Besuche(KonkretesElementB) • KonkreterBesucher – implementiert „Besucher‐Operationen“ • Besucher‐Operation stellt Fragment des Algorithmus der entsprechenden Struktur‐Klasse dar 15.01.2009 Robert Walter ‐ MSI 3 8 / 46 Teilnehmer III Element +NimmEntgegen(Besucher) KonkretesElementA KonkretesElementB +NimmEntgegen(Besucher b) +OperationA() +NimmEntgegen(Besucher b) +OperationB() b->Besuche(this); b->Besuche(this); • Element – definiert eine „NimmEntgegen“‐Operation, die einen Besucher als Argument erwartet • Konkretes Element – implementiert eine „NimmEntgegen“‐Operation und startet den Besuchsvorgang 15.01.2009 Robert Walter ‐ MSI 3 9 / 46 Konsequenzen • • Besucher machen das Hinzufügen neuer Operationen einfach Zusammenführung verwandter Operationen – Trennung von den Operationen, die nichts mit der Besucheraufgabe zu tun haben • • Änderungen der Struktur auf Klassenebene ist schwer Klassenhierarchie übergreifender Besucher – im Vgl. zum Iterator müssen die besuchten Objekte nicht über gemeinsame Oberklasse verfügen • Ansammeln von Zustandsinformationen – Anstatt Parameter‐Forwarding bei Traversierung oder globaler Variablen • Aufbrechung der Kapselung – Besuchte Elemente brauchen starke Schnittstellen (enge Zusammenarbeit zwischen Objekt und Besucher) => Objekt muss eventuell Schnittstellen auf seinen internen Zustand bereitstellen 15.01.2009 Robert Walter ‐ MSI 3 10 / 46 Beispiele I • Besucher‐Muster kann gut in Frameworks integriert werden, wie das JDT (Java Development Toolkit) zeigt – JDT stellt AST (Konkrete Objektstruktur) zu Java‐ Programm bereit – Außerdem auch Besucher‐Schnittstelle • Beispiel zeigt, wie ein Java‐AST in eine neue abstrakte Syntax überführt wird Java‐AST Neue Graph‐Struktur CompilationUnit 15.01.2009 Robert Walter ‐ MSI 3 11 / 46 Beispiele II • JDT stellt bereit – Objektstruktur (über Wurzel vom Typ CompilationUnit) – Besucher‐Schnittstelle (ASTVisitor) • diese muss implementiert werden (=> ASTVisitorImpl2) • Traversierung (Anwendungssteuerung) liegt bei Framework • Visitor kann natürlich auch „von Hand“ implementiert werden... 15.01.2009 Robert Walter ‐ MSI 3 12 / 46 Beispiele III interface Visitor { void visit (Wheel wheel); void visit (Engine engine); void visit (Body body); void visitCar (Car car); } 15.01.2009 Robert Walter ‐ MSI 3 class PrintVisitor implements Visitor { public void visit (Wheel wheel) { println („\tVisiting "+ wheel.getName() + " wheel"); } public void visit (Engine engine) { println („\tVisiting engine"); } public void visit (Body body) { println („\tVisiting body"); } public void visitCar (Car car) { println ("\nVisiting car"); for (CarElement element : car.getElements()) { element.accept(this); } println("Visited car"); } } 13 / 46 Beispiele III interface CarElement { public void accept (Visitor visitor); } class Body implements CarElement{ public void accept (Visitor visitor) { visitor.visit (this); } } class Engine implements CarElement{ public void accept (Visitor visitor) { visitor.visit (this); } } class Wheel implements CarElement { private String name; Wheel (String name) { this.name = name; } String getName() { return name; } public void accept (Visitor visitor) { visitor.visit (this); } } class Car { CarElement[] elements; public CarElement [] getElements(){ return elements.clone(); } public Car() { this.elements = new CarElement[] { new Wheel("front left"), new Wheel("front right"), new Wheel("back left") , new Wheel("back right"), new Body(), new Engine() }; } } 15.01.2009 Robert Walter ‐ MSI 3 14 / 46 Beispiele IV public class VisitorDemo { static public void main (String[] args){ Car car = new Car(); Visitor printVisitor = new PrintVisitor(); printVisitor.visitCar (car); } } • Ausgabe: Visiting Car Visiting front left wheel Visiting front right wheel Visiting back left wheel Visiting back right wheel Visiting body Visiting engine Visited car 15.01.2009 Robert Walter ‐ MSI 3 15 / 46