30

ウィキペディアから:

クラスの実装は、一度完了すると、エラーを修正するためだけに変更できるという考えでした。新しい機能または変更された機能では、別のクラスを作成する必要があります。そのクラスは、継承によって元のクラスのコーディングを再利用できます

私が理解していることから、Visitor パターンは、同じインターフェイスを実装する類似しているが異なるオブジェクトをダブルディスパッチを使用してトラバースするための強力な手法です。私の Java の例の 1 つで、ツリー構造を形成するオブジェクトの複合セットを作成し、それらのオブジェクトのそれぞれの特定の実装が、訪問可能なインターフェースを実装しています。ビジター インターフェイスには、訪問可能なオブジェクトごとにメソッドがあり、具体的なビジターは、これらのケースごとに何をすべきかを実装します。

私が理解しようとしているのは、visitable も実装する複合構造に新しい実装を追加する場合、ビジター インターフェイスを再度開いてそのケースを追加する必要があるという事実です。ビジターの各実装を変更します。

とにかくこれを行う必要があるため (ビジターがビジターブルを理解できない場合、ビジターブルに追加するメリットはありますか?)、これは問題ありませんが、学術レベルでは、これはオープン クローズドの原則に違反していませんか? とにかく、それがデザイン パターンの主な理由の 1 つではありませんか? すべての switch ステートメントを終了するために switch ステートメントを維持する代わりに、このパターンに切り替える適切な理由を示そうとしていますが、switch ブロックの代わりに各ケースのメソッドを使用して、とにかくコードは同じであると誰もが主張しています。そして読みにくい。

4

3 に答える 3

19

パターンは特定の場合に適用できます。GoFの本(p。333)から:

ビジターパターンを使用する場合

  • [...]

  • オブジェクト構造を定義するクラスが変更されることはめったにありませんが、構造に対して新しい操作を定義したい場合がよくあります。オブジェクト構造クラスを変更するには、すべての訪問者へのインターフェイスを再定義する必要がありますが、これにはコストがかかる可能性があります。オブジェクト構造クラスが頻繁に変更される場合は、それらのクラスで操作を定義することをお勧めします。

構造を構成するオブジェクトのクラスを頻繁に変更する場合、Visitorクラス階層を維持するのが難しい場合があります。このような場合、構造を構成するクラスの操作を定義する方が簡単な場合があります。

于 2012-12-20T19:38:05.180 に答える
12

GoFの1人であるJohnVlissidesは、彼のパターンハッチングの本にこの主題に関する優れた章を書いています。彼は、階層を拡張することは、訪問者を無傷に維持することと両立しないという非常に懸念について論じています。彼のソリューションは、訪問者とenumベース(またはタイプベース)のアプローチのハイブリッドであり、訪問者は、訪問者がvisitOtherすぐに理解できる「ベース」階層外のすべてのクラスによって呼び出されるメソッドを提供されます。このメソッドは、訪問者がファイナライズされた後に階層に追加されたクラスを処理するためのエスケープ方法を提供します。

abstract class Visitable {
    void accept(Visitor v);
}
class VisitableSubclassA extends Visitable  {
    void accept(Visitor v) {
        v.visitA(this);
    }
}
class VisitableSubclassB extends Visitable {
    void accept(Visitor v) {
        v.visitB(this);
    }
}
interface Visitor {
    // The "boilerplate" visitor
    void visitB(VisitableSubclassA a);
    void visitB(VisitableSubclassB b);
    // The "escape clause" for all other types
    void visitOther(Visitable other);
}

この変更を追加すると、ソースコードを変更する必要がなく、拡張機能を利用できるため、訪問者はOpen-ClosePrincipleに違反しなくなります。

私はいくつかのプロジェクトでこのハイブリッド方式を試しましたが、それはかなりうまくいきました。私のメインクラス階層は、変更する必要のない、個別にコンパイルされたライブラリで定義されています。の新しい実装を追加するときは、メソッドにこれらの新しいクラスを期待するVisitableように実装を変更します。訪問者と拡張クラスの両方が同じライブラリにあるため、このアプローチは非常にうまく機能します。VisitorvisitOther

PSその質問を正確に議論しているVisitorRevisitedと呼ばれる別の記事があります。著者は、元のビジターパターンではベースのディスパッチに比べて大幅な改善が見られないenumため、ベースのダブルディスパッチに戻ることができると結論付けています。継承階層の大部分がしっかりしていて、ユーザーがあちこちでいくつかの実装を提供することが期待される場合、ハイブリッドアプローチは読みやすさにおいて大きな利点を提供するため、著者には同意しません。比較的簡単に階層に収めることができるクラスがいくつかあるため、すべてを破棄しても意味がありません。enum

于 2012-12-20T19:44:22.940 に答える