1.概要

このチュートリアルでは、行動的なGoFデザインパターンの1つである訪問者を紹介します。

まず、その目的と解決しようとしている問題について説明します。

次に、訪問者のUML図と実際の例の実装を見ていきます。

2.訪問者のデザインパターン

訪問者パターンの目的は、既存のオブジェクト構造に変更を加えることなく新しい操作を定義することです。

/java-composite-pattern




オブジェクトからなるリンクがあると想像してください。オブジェクトの構造は固定されています – 変更できないか、新しいタイプの要素を構造に追加する予定はありません。

では、既存のクラスを変更せずにコードに新しい機能を追加するにはどうすればよいでしょうか。

訪問者のデザインパターンは答えかもしれません。簡単に言えば、訪問者クラスを受け入れる関数を構造体の各要素に追加するだけです。

このようにして、私たちのコンポーネントは、訪問者実装がそれらを「訪問」し、その要素に対して必要なアクションを実行できるようにします。

つまり、クラスからオブジェクト構造に適用されるアルゴリズムを抽出します。

そのため、コードを変更することはありませんが、オープン/クローズの原則をうまく利用できますが、新しいVisitor実装を提供することで、機能を拡張することができます。

3. UML図

リンク:/uploads/Visitor-UML-100×87.png%20100w[]

上のUML図では、2つの実装階層、専門の訪問者、および具体的な要素があります。

まず最初に、クライアントはVisitor実装を使い、それをオブジェクト構造に適用します。複合オブジェクトはそのコンポーネントを反復処理して、訪問者をそれぞれのコンポーネントに適用します。

今、特に関連性があるのは、具象要素

__(ConcreteElementA





ConcreteElementB)が


Visitorを受け入れていること、


単純にそれを

__visitできるようにすることです。

最後に、このメソッドは構造内のすべての要素に対して同じで、訪問者のvisitメソッドに(

this

キーワードを介して)自分自身を渡してhttps://en.wikipedia.org/wiki/Double__dispatch[double dispatch]を実行します。

4.実装

私たちの例はJSONとXML具象要素からなるカスタムの

__Document


オブジェクトです。要素は共通の抽象スーパークラス、

Element.__を持ちます。


Document

クラス:

public class Document extends Element {

    List<Element> elements = new ArrayList<>();

   //...

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


Element

クラスは、

__ Visitor

__インタフェースを受け入れる抽象メソッドを持ちます。

public abstract void accept(Visitor v);

したがって、新しい要素を作成するときは、

JsonElement

という名前を付けて、このメソッドの実装を提供する必要があります。

ただし、Visitorパターンの性質上、実装は同じになるため、ほとんどの場合、他の既存の要素から定型コードをコピーアンドペーストする必要があります。

public class JsonElement extends Element {

   //...

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

私たちの要素はあらゆる訪問者がそれらを訪問することを可能にするので、私たちは私たちの

__Document

__elementを処理したいとしましょう、しかしそれらのそれぞれはそのクラス型によって異なる方法で。

したがって、私たちの訪問者は与えられたタイプのために別のメソッドを持つことになります。

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);
    }
}

ここでは、具体的な訪問者が2つのメソッドを実装しています。

これにより、必要なアクションを実行できる構造の特定のオブジェクトにアクセスできます。

5.テスト

テスト目的で、

__ VisitorDemo

__クラスを見てみましょう。

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);
    }

   //...
}

まず、

__Element

__Visitorを作成します。これは、要素に適用するアルゴリズムを保持しています。

次に、適切なコンポーネントを使用して____Documentを設定し、オブジェクト構造のすべての要素によって受け入れられるビジターを適用します。

出力は次のようになります。

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

これは、訪問者が

__Element

__typeに応じて構造の各要素を訪問したことを示し、適切なメソッドに処理を振り分け、すべての基礎となるオブジェクトからデータを取得できることを示しています。

6.欠点

各デザインパターンとして、訪問者でさえもマイナス面があります。特に、オブジェクトの構造に新しい要素を追加する必要がある場合は、その使用方法によってコードの管理が難しくなります


たとえば、新しい

YamlElementを追加した場合、

この要素を処理するために必要な新しいメソッドを使用して、既存のすべての訪問者を更新する必要があります。さらに続けて、10人以上の具体的な訪問者がいる場合、それらすべてを更新するのは面倒かもしれません。

これ以外に、このパターンを使用すると、1つの特定のオブジェクトに関連するビジネスロジックがすべての訪問者の実装に広がります。

7.まとめ

Visitorパターンは、それが動作するクラスからアルゴリズムを分離するのに最適です。それに加えて、それはちょうど訪問者の新しい実装を提供することによって、新しい操作を追加することをより簡単にします。

さらに、コンポーネントのインターフェースに頼る必要はありません。それらが異なっていても、具象エレメントごとに個別の処理アルゴリズムがあるため、問題ありません。

さらに、訪問者は、移動した要素に基づいて最終的にデータを集計できます。

より特別なバージョンの訪問者デザインパターンを見るには、リンクをチェックしてください:/java-nio2-file-visitor[Java NIOの訪問者パターン] – JDKのパターンの使用法。

いつものように、完全なコードはhttps://github.com/eugenp/tutorials/tree/master/patterns/design-patterns[Githubプロジェクト]にあります。