Besucher-Entwurfsmuster in Java

Besucherdesignmuster in Java

1. Überblick

In diesem Tutorial stellen wir eines der verhaltensbezogenen GoF-Entwurfsmuster vor - den Besucher.

Zunächst erklären wir den Zweck und das Problem, das es zu lösen versucht.

Als nächstes werfen wir einen Blick auf das UML-Diagramm des Besuchers und die Implementierung des praktischen Beispiels.

2. Besucher-Entwurfsmuster

Der Zweck eines Besuchermusters besteht darin, eine neue Operation zu definieren, ohne Änderungen an einer vorhandenen Objektstruktur vorzunehmen.

Stellen Sie sich vor, wir haben einen Link: / java-compos-pattern object, der aus Komponenten besteht. Die Struktur des Objekts ist festgelegt. Entweder können wir sie nicht ändern, oder wir planen nicht, der Struktur neue Elementtypen hinzuzufügen.

Wie können wir nun unserem Code neue Funktionen hinzufügen, ohne die vorhandenen Klassen zu ändern?

Das Besucher-Designmuster könnte eine Antwort sein. Einfach ausgedrückt,we’ll have to do is to add a function which accepts the visitor class to each element of the structure.

Auf diese Weise ermöglichen unsere Komponenten der Besucherimplementierung, sie zu „besuchen“ und alle erforderlichen Aktionen für dieses Element auszuführen.

Mit anderen Worten, wir extrahieren den Algorithmus, der auf die Objektstruktur angewendet wird, aus den Klassen.

Infolgedessen werden wir den Code nicht ändern, aber wir können die Funktionalität trotzdem erweitern, indem wir eine neue Vereinfachung vonVisitor bereitstellen.

3. UML-Diagramm

image

Im obigen UML-Diagramm haben wir zwei Implementierungshierarchien, Fachbesucher und konkrete Elemente.

Zunächst verwendet der Client eine Visitor-Implementierung und wendet diese auf die Objektstruktur an. Das zusammengesetzte Objekt durchläuft seine Komponenten und wendet den Besucher auf jede Komponente an.

Besonders relevant ist nun, dassconcrete elements (ConcreteElementA and ConcreteElementB) are accepting a Visitor, simply allowing it to visit them.

Schließlich ist diese Methode für alle Elemente in der Struktur gleich. Sie führtdouble dispatch aus, indem sie sich selbst (über das Schlüsselwortthis) an die Besuchsmethode des Besuchers übergibt.

4. Implementierung

Unser Beispiel wird ein%(t0)sobject sein, das aus konkreten JSON- und XML-Elementen besteht; die Elemente haben eine gemeinsame abstrakte Superklasse, dieElement.

Die KlasseDocument:

public class Document extends Element {

    List elements = new ArrayList<>();

    // ...

    @Override
    public void accept(Visitor v) {
        for (Element e : this.elements) {
            e.accept(v);
        }
    }
}

Die KlasseElementhat eine abstrakte Methode, die dieVisitor -Sinterschnittstelle akzeptiert:

public abstract void accept(Visitor v);

Wenn Sie das neue Element erstellen, nennen Sie es daherJsonElement. Wir müssen die Implementierung dieser Methode bereitstellen.

Aufgrund der Art des Besuchermusters ist die Implementierung jedoch dieselbe. In den meisten Fällen müssten wir daher den Code aus einem anderen, bereits vorhandenen Element kopieren und einfügen:

public class JsonElement extends Element {

    // ...

    public void accept(Visitor v) {
        v.visit(this);
    }
}

Da unsere Elemente es jedem Besucher ermöglichen, sie zu besuchen, nehmen wir an, dass wir unsereDocument -Auswahlen verarbeiten möchten, aber jede davon je nach Klassentyp auf unterschiedliche Weise.

Aus diesem Grund hat unser Besucher für den angegebenen Typ eine separate Methode:

public class ElementVisitor implements Visitor {

    @Override
    public void visit(XmlElement xe) {
        System.out.println(
          "processing an XML element with uuid: " + xe.uuid);
    }

    @Override
    public void visit(JsonElement je) {
        System.out.println(
          "processing a JSON element with uuid: " + je.uuid);
    }
}

Hier implementiert unser konkreter Besucher zwei Methoden, entsprechend eine für jeden Typ derElement.

Dies gibt uns Zugriff auf das bestimmte Objekt der Struktur, an dem wir die erforderlichen Aktionen ausführen können.

5. Testen

Schauen wir uns zu Testzwecken dieVisitorDemo-Skala an:

public class VisitorDemo {

    public static void main(String[] args) {

        Visitor v = new ElementVisitor();

        Document d = new Document(generateUuid());
        d.elements.add(new JsonElement(generateUuid()));
        d.elements.add(new JsonElement(generateUuid()));
        d.elements.add(new XmlElement(generateUuid()));

        d.accept(v);
    }

    // ...
}

Zuerst erstellen wir einenElementVisitor, der den Algorithmus enthält, den wir auf unsere Elemente anwenden werden.

Als nächstes richten wir unserDocument mit den richtigen Komponenten ein und wenden den Besucher an, der von jedem Element einer Objektstruktur akzeptiert wird.

Die Ausgabe wäre wie folgt:

processing a JSON element with uuid: fdbc75d0-5067-49df-9567-239f38f01b04
processing a JSON element with uuid: 81e6c856-ddaf-43d5-aec5-8ef977d3745e
processing an XML element with uuid: 091bfcb8-2c68-491a-9308-4ada2687e203

Es zeigt, dass der Besucher jedes Element unserer Struktur besucht hat, abhängig vomElement -Styp, die Verarbeitung an die entsprechende Methode gesendet hat und die Daten von jedem zugrunde liegenden Objekt abrufen konnte.

6. Nachteile

Bei jedem Entwurfsmuster hat sogar der Besucher seine Nachteile, insbesondere seine Nutzung makes it more difficult to maintain the code if we need to add new elements to the object’s structure.

Wenn wir beispielsweise neueYamlElement,hinzufügen, müssen wir alle vorhandenen Besucher mit der neuen Methode aktualisieren, die für die Verarbeitung dieses Elements gewünscht wird. Wenn wir dann noch zehn oder mehr konkrete Besucher haben, ist es möglicherweise mühsam, alle zu aktualisieren.

Wenn Sie dieses Muster verwenden, wird die Geschäftslogik, die sich auf ein bestimmtes Objekt bezieht, auf alle Besucherimplementierungen verteilt.

7. Fazit

Das Besuchermuster ist großartig, um den Algorithmus von den Klassen zu trennen, mit denen er arbeitet. Darüber hinaus vereinfacht es das Hinzufügen neuer Operationen, indem lediglich eine neue Implementierung des Besuchers bereitgestellt wird.

Darüber hinaus sind wir nicht auf Komponentenschnittstellen angewiesen. Wenn diese unterschiedlich sind, ist dies in Ordnung, da wir einen separaten Algorithmus für die Verarbeitung pro konkretem Element haben.

Darüber hinaus kann der Besucher Daten basierend auf dem Element aggregieren, das er durchquert.

Um eine speziellere Version des Besucherentwurfsmusters anzuzeigen, überprüfen Sievisitor pattern in Java NIO - die Verwendung des Musters im JDK.

Wie üblich ist der vollständige Code auf denGithub project verfügbar.