web-dev-qa-db-ja.com

なぜビジターパターンを使用するのですか?

重複: ビジターデザインパターンをいつ使用する必要があるか

なぜ誰かがビジターパターンを使いたいのでしょうか?いくつかの記事を読みましたが、何も得られません。

カスタムを請求する機能が必要な場合は、

Custom.Accept(BillVisitor)

またはのようなもの

Bill(Customer)

2つ目はそれほど複雑ではなく、Bill関数は引き続きCustomerクラスから分離されています。では、なぜビジターパターンを使用したいのでしょうか。

27
Tired

この問題は、複雑な構造、つまり階層など、単純に線形ではないものがある場合に発生します。構造を単純に繰り返すことができない場合、訪問者は非常に便利です。

階層(またはツリー)がある場合、各Nodeには子のリストがあります。ツリー内のすべてのノードにプロセスを適用する場合は、Visitorを作成すると便利です。

A Nodeは、Visitorをそれ自体とその子ノードのそれぞれに適用できます。各子は推移的に同じことを行います(Visitorをそれ自体に適用してからすべての子に適用します)。

ビジターのこの使用は非常にうまくいきます。

非常に単純なデータ構造の場合、Visitorはあまり価値を付加しません。

49
S.Lott

ビジターパターンは、多重ディスパッチを直接サポートしない言語のハックです(C++やJavaはオブジェクトに基づく単一ディスパッチのみをサポートします。複数ディスパッチはCLOSでサポートされます)。この場合、 BillとCustomerの両方をポリモーフィックにする場合は、単一のディスパッチ言語でBillVisitorとCustomerの2つのインターフェイスを使用する必要があります。例:acceptの実装は通常次のとおりです。

void accept(BillVisitor visitor) { visitor.bill(this); } // Java syntax

ここで、Customer#acceptとBillVisitor#billの両方をそれぞれのサブクラスでオーバーライドできるため、他の方法では実現できない実行時の動作の非常に豊富な組み合わせが得られることに注意してください。これは、複雑なデータ構造に機能を適用するためのクロージャの代わりとして、他のほとんどの人がここで説明したもののスーパーセットです。

15
ididak

CAD/CAMアプリケーションには、パスとパスのコレクション(PathList)があります。時々私は形のコレクションを生成しなければなりません。この特定の形状のコレクションを生成する必要があるだけでなく、別の形状のコレクションに含める必要があります。計算に必要なすべての情報を伝えるために、多くの場合、12個以上のパラメーターが必要です。

私のCAMのすべての形状計算(単純または複雑)は、さまざまなマシンに送信されるPathListに送られます。

この設計では、結果を単一のコレクションに追加することを含むいくつかの設定を行うのが最善です。

パスビジターはこれにうまく適合します。各形状計算は、必要に応じて複雑なプロパティを持つ独自のクラスにカプセル化されます。

したがって、キッチンフードの訪問者はパスリストに8つの形を持っている可能性があります

キッチンカウンタービジターは6つの形を追加します。

引き出しビジターはさらにいくつか追加します。

次に、他のシェイプジェネレーターと同様に、結果のPathListをシステムの残りの部分に渡します。

別の訪問者を追加するか、一部を実行しないことで、簡単にオプションを利用できます。カウンターと引き出しだけが欲しい人のために、私は2人の訪問者を走らせる必要があるだけです。

結果のコードは非常に読みやすく、3年後、5年後、または10年後にこの領域を再検討するときに重要です。さらに、1人の訪問者に対するカプセル化の変更により、他の訪問者への影響は最小限に抑えられます。

さらに、ビジターパターンを実装することで、PathListに新しい機能を追加するための標準化されたデザインパターンができました。

たとえば、以前は、プロパティエディタは単一のパスでしか機能しませんでした。複数のパスの編集に変更したとき、カスタムビジターを実装してすべてのパスにグローバルな変更を加えるのは簡単でした。デリゲート内のforループを使用するよりも読みやすくなりました。

しかし、最終的には判断と好みに帰着します。

3
RS Conley

訪問者のもう1つの素晴らしい特典は、拡張が簡単であり、言語で許可されている場合は、lamdbasを​​使用してクリーンアップすることもできます。

2
Robert Gould

どちらの場合も、訪問者は顧客クラスから分離されます。呼び出し元クラスからも訪問者を抽象化する場合に利点があります。 2番目のケースでは、呼び出し側クラスは課金について知っている必要があります。代わりに、IVisitorを返す別のルーチンをどこかに置くことができます。呼び出し元のコードはCustom.Accept(IVisitor)を呼び出すことができ、訪問者が何をしているかについては何も知りません。

基本的に、この場合とS.Lottが言及した場合、訪問者は代理人のように考えることができます。オブジェクトのように受け渡し、必要な場所で使用できる関数です。

1
Jacob Adams