メソッドとクラスの両方に拡張できる二重ディスパッチを実装する方法を探しています。
これまでは、基本的に次の 3 つのアプローチを使用していました。
- 優れた従来の手続き型アプローチ
switch
(新しい関数を追加するのは簡単で、新しいクラスを追加するのは難しい) - 訪問者パターン (非常に似ています: 新しい訪問者を追加するのは簡単ですが、新しいクラスを追加するのは難しいです)
- シンプルなインターフェイス アプローチ (新しいクラスを追加するのは簡単ですが、新しい関数を追加するのは難しい)
関数や既存のクラスを変更せずに、新しい関数と新しいクラスの両方を追加できる方法を探しています。
これは、オブジェクト/関数の特定の組み合わせを要求しても失敗しないはずです。少なくとも、プログラムの起動後に一度実行できるチェックの後ではありません。
これまでに使用したアプローチは次のとおりです。
従来の手続き型アプローチ:
enum WidgetType {A,B,C,}
interface IWidget
{
WidgetType GetWidgetType();
}
class WidgetA
{
public WidgetType GetWidgetType() {return WidgetType.A;}
}
class WidgetB
{
public WidgetType GetWidgetType() {return WidgetType.B;}
}
class WidgetC
{
public WidgetType GetWidgetType() {return WidgetType.C;}
}
// new classes have to reuse existing "WidgetType"s
class WidgetC2
{
public WidgetType GetWidgetType() {return WidgetType.C;}
}
class Functions
{
void func1(IWidget widget)
{
switch (widget.GetWidgetType())
{
case WidgetType.A:
...
break;
case WidgetType.A:
...
break;
case WidgetType.A:
...
break;
default:
// hard to add new WidgetTypes (each function has to be augmented)
throw new NotImplementedException();
}
}
// other functions may be added easily
}
従来のオブジェクト指向アプローチ (ビジター パターン):
interface IWidgetVisitor
{
void visit(WidgetA widget);
void visit(WidgetB widget);
void visit(WidgetC widget);
// new widgets can be easily added here
// but all visitors have to be adjusted
}
interface IVisitedWidget
{
void accept(IWidgetVisitor widgetVisitor);
}
class WidgetA : IVisitedWidget
{
public void accept(IWidgetVisitor widgetVisitor){widgetVisitor.visit(this);}
public void doStuffWithWidgetA(){}
}
class WidgetB : IVisitedWidget
{
public void accept(IWidgetVisitor widgetVisitor){widgetVisitor.visit(this);}
public void doStuffWithWidgetB(){}
}
class WidgetC : IVisitedWidget
{
public void accept(IWidgetVisitor widgetVisitor){widgetVisitor.visit(this);}
public void doStuffWithWidgetB(){}
}
class SampleWidgetVisitor : IWidgetVisitor
{
public void visit(WidgetA widget){ widget.doStuffWithWidgetA(); }
public void visit(WidgetB widget){ widget.doStuffWithWidgetB(); }
public void visit(WidgetC widget){ widget.doStuffWithWidgetC(); }
}
シンプルなインターフェースアプローチ:
IWidget
{
void DoThis();
void DoThat();
// if we want to add
// void DoOtherStuff();
// we have to change each class
}
WidgetA : IWidget
{
public void DoThis(){ doThisForWidgetA();}
public void DoThat(){ doThatForWidgetA();}
}
WidgetB : IWidget
{
public void DoThis(){ doThisForWidgetB();}
public void DoThat(){ doThatForWidgetB();}
}
WidgetC : IWidget
{
public void DoThis(){ doThisForWidgetC();}
public void DoThat(){ doThatForWidgetC();}
}