我正在寻找一种方法来实现可以为方法和类扩展的双重调度。
到目前为止,我基本上使用了三种方法:
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();}
}
答案 0 :(得分:0)
这真的归结为你看到代码最不稳定的地方。我想我会选择Widgets派生的基类,每个函数都标记为virtual
,因此添加一个新函数并不要求所有派生类都提供一个实现,如果你的代码不会失败,你在一个没有提供Widget特定实现的具体类上调用该函数。
答案 1 :(得分:0)
我遇到了类似的问题 - 本质上问题是多次调度,单调度OO语言不能很好地支持。
我所遇到的妥协是对您的程序示例的可扩展变体。
它使用带有字典的Mediator(或Coordinator)来注册和解决两个对象之间应该发生的操作。在下面的代码示例中,我使用了两个对象之间的冲突问题。
基本结构是:
enum CollisionGroup { Bullet, Tree, Player }
interface ICollider
{
CollisionGroup Group { get; }
}
Mediator对象定义如下:
class CollisionResolver
{
Dictionary<Tuple<CollisionGroup, CollisionGroup>, Action<ICollider, ICollider>> lookup
= new Dictionary<Tuple<CollisionGroup, CollisionGroup>, Action<ICollider, ICollider>>();
public void Register(CollisionGroup a, CollisionGroup b, Action<ICollider, ICollider> action)
{
lookup[Tuple.Create(a, b)] = action;
}
public void Resolve(ICollider a, ICollider b)
{
Action<ICollider, ICollider> action;
if (!lookup.TryGetValue(Tuple.Create(a.Group, b.Group), out action))
action = (c1, c2) => Console.WriteLine("Nothing happened..!");
action(a, b);
}
}
呸!它看起来不那么好但主要是由于泛型类型和缺少支持对象。我没有为这个例子做任何事情,因为这会给这个答案的范围带来太多的复杂性。
对象的使用方式如下:
var mediator = new CollisionResolver();
mediator.Register(CollisionGroup.Bullet, CollisionGroup.Player,
(b, p) => Console.WriteLine("A bullet hit {0} and it did not end well", p));
mediator.Register(CollisionGroup.Player, CollisionGroup.Tree,
(p, t) => Console.WriteLine("{0} ran into a tree. Ouch", p));
mediator.Register(CollisionGroup.Player, CollisionGroup.Player,
(p1, p2) => Console.WriteLine("{0} and {1} hi-fived! Yeah! Awesome!", p1, p2));
var jeffrey = new Player("Jeffrey");
var cuthbert = new Player("Cuthbert");
var bullet = new Bullet();
var tree = new Tree();
mediator.Resolve(jeffrey, cuthbert); // Jeffrey and Cuthbert hi-fived! Yeah! Awesome!
mediator.Resolve(jeffrey, tree); // Jeffrey ran into a tree. Ouch
mediator.Resolve(bullet, cuthbert); // A bullet hit Cuthbert and it did not end well
mediator.Resolve(bullet, tree); // Nothing happened..!
这种方法是我能找到的最具扩展性的方法。要添加新反应或新类型,只需要新的枚举成员和.Register()
方法调用。
扩展上述方法的要点:
DispatchMediator<TType, TEnum>
可以轻松实现Tuple<T, T>
和Action<T, T>
类型以接受单个类型参数ICollider
界面更改为通用界面