はじめに
どうやら、私はプログラマーとしての人生全体で、「型にはまらない」ビジター パターンを実行してきたようです。
はい、Visitor のメソッドから具体的な複合要素の Visit メソッドにディスパッチしVisit
ます。
これが私が学んだ方法だと思いますが、今ではその例を見つけることができず、学んだソースがなくなりました。
さて、具体的な要素のディスパッチが複合要素のAccept
メソッドに組み込まれているという圧倒的な証拠に直面して、私が行ってきた方法に少なくとも何らかの利点があるかどうか疑問に思っています。私に見える2つの利点は次のとおりです。
- ディスパッチ方法を決定する場所は 1 か所しかありません。ベース ビジターです。
- 新しい複合要素タイプを追加して、基本ビジターにそれらを無視させることができますが、派生ビジターは
Visit
それらを処理するためにオーバーライドできます。
例
基本的なコンポジット/ビジター モデルは次のとおりです。
// "Unorthodox" version
public class BaseVisitor
{
public virtual void Visit(CompositeElement e)
{
if(e is Foo)
{
VisitFoo((Foo)e);
}
else if(e is Bar)
{
VisitBar((Bar)e);
}
else
{
VisitUnknown(e);
}
}
protected virtual void VisitFoo(Foo foo) { }
protected virtual void VisitBar(Bar bar) { }
protected virtual void VisitUnknown(CompositeElement e) { }
}
public class CompositeElement
{
public virtual void Accept(BaseVisitor visitor) { }
}
public class Foo : CompositeElement { }
public class Bar : CompositeElement { }
訪問者クラスは、正規バージョンではなく、2 番目の型ベースのディスパッチを担当するようになったことに注意してください。たとえば、次のFoo
ようになります。
// Canonical visitor pattern 2nd dispatch
public override void Accept(BaseVisitor visitor)
{
visitor.VisitFoo(this);
}
さて、ディフェンスですが…
メリット1
新しい CompositeElement タイプを追加したいとしましょう:
public class Baz : CompositeElement { }
ビジター モデルでこの新しい要素タイプに対応するには、BaseVisitor クラスに変更を加えるだけです。
public class BaseVisitor
{
public virtual void Visit(CompositeElement e)
{
// Existing cases elided...
else if(e is Baz)
{
VisitBaz((Baz)e);
}
}
protected virtual void VisitBaz(Foo foo) { }
}
if
確かに、これは小さな問題ですが、メンテナンスが簡単になるようです (つまり、大きな問題やswitch
ステートメントを気にしない場合)。
メリット2
コンポジットを別のパッケージで拡張したいとしましょう。を変更せずにこれに対応できますBaseVisitor
。
public class ExtendedVisitor : BaseVisitor
{
public override Visit(CompositeElement e)
{
if(e is ExtendedElement)
{
VisitExtended((ExtendedElement)e);
}
else
{
base.Visit(e);
}
}
protected virtual void VisitExtended(ExtendedElement e) { }
}
public class ExtendedCompositeElement : CompositeElement { }
この構造を持つことで、拡張された CompositeElement 型に対応するためにBaseVisitor
必要な依存関係を断ち切ることができます。VisitExtended
結論
現時点では、Visitor パターンを十分に実装していないか、十分に長く維持していないため、不利な点が私に重くのしかかっています。明らかに、大きな switch ステートメントを維持するのは苦痛であり、パフォーマンスへの影響もありますが、BaseVisitor
拡張機能に依存しないようにする柔軟性を上回るかどうかはわかりません。
マイナス面については、あなたの考えを考慮してください。