Besucher (Entwurfsmuster)

Der Besucher (englisch visitor oder visitor pattern) ist ein Entwurfsmuster aus dem Bereich der Softwareentwicklung, das zur Kategorie der Verhaltensmuster (engl. behavioral patterns) gehört. Das Muster ist eines der sogenannten GoF-Entwurfsmuster. Es dient der Kapselung von Operationen. Die Operationen können auf Elemente einer Objektstruktur ausgeführt[1] werden. Durch die Kapselung ist es möglich, dass neue Operationen ohne Veränderung der betroffenen Elementklassen definiert werden.

Mit Hilfe des Besuchers werden einfache Multimethoden (double dispatch) in einer OOP-Sprache implementiert, die von Haus aus nur single dispatch unterstützt.

Oft ist es schwierig, nicht miteinander verwandte Operationen in die Klassen einer Objektstruktur zu integrieren. Bei der Erweiterung um neue Operationen müssen alle Klassen erweitert werden. Das Besucher-Entwurfsmuster lagert die Operationen in externe Besucherklassen aus. Dazu müssen die zu besuchenden Klassen eine Schnittstelle zum Empfang eines Besuchers definieren.

Generell empfiehlt sich die Verwendung des Besucher-Entwurfsmusters, wenn

  • viele unterschiedliche, nicht verwandte Operationen auf einer Objektstruktur realisiert werden sollen,
  • sich die Klassen der Objektstruktur nicht verändern,
  • häufig neue Operationen auf der Objektstruktur integriert werden müssen oder
  • ein Algorithmus über die Klassen einer Objektstruktur verteilt arbeitet, aber zentral verwaltet werden soll.
UML-Diagramm für das Entwurfsmuster "Besucher"(visitor)
UML-Diagramm für das Entwurfsmuster "Besucher"(visitor)
  • Besucher (abstrakt)
    • deklariert für jede Klasse konkreter Elemente eine Besuchsfunktion
  • Besucher1,… (konkret)
    • implementiert Besuchsfunktionen
    • Jede Besuchsfunktion ist ein Teil des Algorithmus, der auf die gesamte Objektstruktur angewendet wird.
    • Lokaler Zustand dient als Kontext für den Algorithmus.
  • Element (abstrakt)
    • deklariert eine Operation zum Empfang eines Besuchers
  • ElementA,… (konkret)
    • implementiert den Empfang eines Besuchers
  • Objektstruktur
  • Neue Operationen lassen sich leicht durch die Definition neuer Besucher hinzufügen.
  • Verwandte Operationen werden im Besucher zentral verwaltet und von besucherfremden Operationen getrennt.
  • Besucher können mit Objekten aus voneinander unabhängigen Klassenhierarchien arbeiten.
  • Die gute Erweiterungsmöglichkeit der Klassen von Besuchern muss mit einer schlechten Erweiterbarkeit der Klassen der konkreten Elemente erkauft werden. Müssen neue konkrete Elemente hinzugefügt werden, so führt dies dazu, dass viele Besucher-besuche-Methoden implementiert werden müssen.

Virtuelles Reisebüro

[Bearbeiten | Quelltext bearbeiten]

Ein Reiseveranstalter bietet seinen Kunden verschiedene Busreisen, Ferienhäuser und Mietwagen an. Jedem Objekt sind eine Beschreibung und eine Preiskategorie für Sommer und Winter zugewiesen. Die Preise der Kategorien sind in einem Preismodul gespeichert. Bei Ferienhäusern sind darüber hinaus Bilder, bei Mietwagen technische Daten abgelegt. Sowohl die Klassen für Busreisen, Ferienhäuser und Mietwagen, als auch das Preismodul bieten eine Schnittstelle zum Empfang eines Besuchers. Das Preismodul ist außerhalb der Klassenhierarchie von Busreisen, Ferienhäusern und Mietwagen.

Ein Kunde kann sich nun eine Reise zusammenstellen. Fragt er dann nach dem Gesamtpreis, so besucht ein Besucher zunächst die interessierenden Objekte, fragt die jeweilige Kategorie ab. Für jede Kategorie verwaltet er einen lokalen Zähler. Zuletzt besucht er das Preismodul und berechnet auf Grund der dort abgelegten Preise und seiner lokal gesammelten Informationen den Gesamtpreis.

Entscheidet sich der Kunde, die Reise zu buchen, kann ein anderer Besucher eine Reisebestätigung erstellen. Dazu besucht er wieder die den Kunden interessierenden Objekte und das Preismodul. Sein lokaler Zustand besteht aus einem Dokument, das er gemäß den Informationen der Objekte gestaltet. Bei allen Objekten listet er zunächst die Beschreibung und die Preiskategorie auf, bei Mietwagen zusätzlich die technischen Daten. Beim Besuch des Preismoduls ergänzt er dann die einzelnen Beschreibungen um die konkreten Preise.

Beide Besucher übergreifen Klassenhierarchien, da sie sowohl auf der Klassenhierarchie der buchbaren Reiseelemente als auch auf dem Preismodul arbeiten.

Besucher im Compilerbau

[Bearbeiten | Quelltext bearbeiten]

Im Compilerbau liegt nach der syntaktischen Analyse meist ein abstrakter Syntaxbaum vor. Ein solcher Baum lässt sich durch Klassen für die verschiedenen Elemente und Verwendung von Aggregationen gut als Objektstruktur beschreiben. Auf dieser Objektstruktur kann man nun einen allgemeinen Besucher definieren, der den Baum traversiert. Dazu werden bei der Implementierung der Besuchsfunktion für eine Elementklasse des Baums die aggregierten Elemente nacheinander besucht. Von diesem allgemeinen Besucher lassen sich nun verschiedene Besucher ableiten, die unterschiedliche Operationen auf dem abstrakten Syntaxbaum implementieren.

In einem Besucher lässt sich die semantische Analyse realisieren. Dazu besucht dieser die Elemente des Baums und erweitert die Symboltabelle um Informationen zu Datentypen von Variablen und Routinen oder überprüft Ausdrücke unter Einbeziehung der Symboltabelle, ob sie wohltypisiert sind. Je nach den Eigenschaften der Quellsprache muss die Sammlung von Informationen und die Typprüfung auch auf zwei Besucher verteilt werden.

Ein weiterer Besucher kann dann die Synthese des Zielcodes realisieren. Auch dieser besucht dazu die einzelnen Elemente und sammelt die Zielcodefragmente in seinem lokalen Zustand. Abhängig von der Klasse des besuchten Elements kann er dann bereits gesammelte Fragmente zu größeren kombinieren.

Weitere Besucher können Debuginformationen sammeln oder Codeoptimierungen auf Quellcodebasis durchführen. Alle Besucher können dabei auf die Besuchsfunktionen des allgemeinen Besuchers zurückgreifen, wenn ein Element ohne weitere Operationen nur traversiert werden soll. Auch der Zielcode kann zunächst wiederum in einer Baumstruktur erzeugt werden, um dann verschiedene Optimierungen in unterschiedlichen Besuchern zu realisieren.

Programmierbeispiel in PHP

[Bearbeiten | Quelltext bearbeiten]
abstract class Element {
    abstract function entgegennehmen(Besucher $besucher);
    abstract function getName();
}

class ElementA extends Element {
    private $info_A;
    public function __construct($text1, $text2) {
        $this->info_A = '['.$text1.'--'.$text2.']';
    }
    public function getName() {
        return 'A';
    }
    public function getInfo() {
        return $this->info_A;
    }
    public function entgegennehmen(Besucher $besucher) {
        $besucher->BesuchVonElementA($this);
    }
}

class ElementB extends Element {
    private $the_data;
    public function __construct($text) {
        $this->the_data = '('.$text.')';
    }
    public function getName() {
        return 'B';
    }
    public function getData() {
        return $this->the_data;
    }
    public function entgegennehmen(Besucher $besucher) {
        $besucher->BesuchVonElementB($this);
    }
}

abstract class Besucher {
    abstract function BesuchVonElementA(ElementA $elem);
    abstract function BesuchVonElementB(ElementB $elem);
}

class Besucher1 extends Besucher {
    private $characteristics;
    public function getCharacs() {
        return $this->characteristics;
    }
    public function BesuchVonElementA(ElementA $elem) {
        $this->characteristics = 'Info:'.$elem->getInfo();
    }
    public function BesuchVonElementB(ElementB $elem) {
        $this->characteristics = 'DATA:'.$elem->getData().'!!';
    }
}

function Test() {
    write_line('Testanfang');

    // Objektstruktur
    $elemente = array (
        new ElementA('Hallo', 'Neu!!'),
        new ElementB('Endlich.'),
    );

    $bes1 = new Besucher1();
    foreach ($elemente as $element) {
        $element->entgegennehmen($bes1);
        write_line('Nach Besuch von Element '.$element->getName().': '.$bes1->getCharacs());
    }
}

function write_line($text) {
    print $text.'<br/>';
}

Test();

Ausgabe:

Testanfang
Nach Besuch von Element A: Info:[Hallo--Neu!!]
Nach Besuch von Element B: DATA:(Endlich.)!!

Verwandte Entwurfsmuster

[Bearbeiten | Quelltext bearbeiten]
  • Kommando. Das Kommando kapselt wie der Besucher eine oder mehrere Funktionen in einem Objekt, um diese an einen Aufrufer zuzustellen. Im Gegensatz zum Besucher enthält das Kommando kein Prinzip zur Traversierung einer Objektstruktur.
  • Iterator. Der Iterator definiert ein Traversierungsprinzip wie auch der Besucher, macht aber keine Typunterscheidung bei den traversierten Objekten.

Beziehung zu anderen Entwurfsmustern

[Bearbeiten | Quelltext bearbeiten]
  • Kompositum. Wenn die Typen in einer Kompositstruktur ausreichend stabil definiert sind, bietet sich ein Visitor zur Bearbeitung der Struktur an.

Einzelnachweise

[Bearbeiten | Quelltext bearbeiten]
  1. Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides: Entwurfsmuster. 5. Auflage. Addison-Wesley, 1996, ISBN 3-8273-1862-9, S. 301.