今天在看文章的时候遇到了multiple dispatch这个术语。看看了wiki,写得云里雾里。最后搜了搜资料,基本上搞清楚了。
multiple dispatch的混淆之处在于其和object system结合在一起。当然wiki上写得很清楚:“a function or method can be dynamically dispatched based on the run time (dynamic) type of more than one of its arguments.”。这摆明了就是和object system紧耦合在了一起。更准确地来讲multiple dispath其实就是专门针对OO中的消息通信的一个概念。与之相关的概念还有single dispatch和dynamic dispatch。
如 果不考虑object system,我觉得function overloading也可以算是一种multiple dispatch。function overloading需要根据参数的类型以及个数来决议调用函数,过程上来讲和multiple dispatch的定义很像,都是通过参数类型来决定调用函数。但是有一个很重要的不同点:multiple dispatch是runtime行为,而function overloading是编译器在compile-time时就决议好了。
要彻底理解multiple dispatch还要从OO的定义说起:OO其实就是对象以及对象间的通信。C++对于对象间的通信采用了函数调用的方式,这与objective-c有点区别。C++的通信方式可以简单地以object.method()的方式呈现。method其实就是信息,object.method()的意思就是method这个信息被派发给了object。这就是所谓有single dispatch,因为信息只能被派发给一个对象。
先举一个简单的例子。有一个状态机S会接受N种不同的trigger,每个trigger要触发当前状态下的一个动作。
class AbstractTrigger; class AbstractState { public:virtual void AcceptTrigger( AbstractTrigger* _trigger ) = 0; };class AbstractTrigger {/*....*/ };
class State1 : public AbstractState
{
public:
void AcceptTrigger( AbstractTrigger* _trigger )
{
/*....*/
}
};
class Trigger1 : public AbstractTrigger
{
/*....*/
};
目前的难点在于如何根据trigger的类型来选择合适的动作。一种可行的方法是在State1::AcceptTrigger中判断_trigger的实际类型,然后再根据类型去执行相应的动作。这种作法属于过街老鼠式的做法,code review八成是过不去的。另外一种做法是采用visitor pattern的做法,代码如下:
class AbstractTrigger; class Trigger1; class AbstractState { public:virtual void AcceptTrigger( AbstractTrigger* _trigger ) = 0;virtual void Action1( void ) = 0; };class AbstractTrigger { public:virtual void Action( AbstractState* _state ) = 0; };class State1 : public AbstractState {void AcceptTrigger( AbstractTrigger* _trigger ){_trigger->Action }void Action1( void ){/*....*/} };class Trigger1 : public AbstractTrigger { public:void Action( AbstractState* _state ){_state->Action1();} };
我们在AbstractState中声明了纯虚函数Action1,子类实现它以完成Trigger1时的动作。当然也可以加入Action2, Action3 ... ActionN,分别对应Trigger2, Trigger3 ... TriggerN。在State1::AcceptTrigger中将_trigger派发给了Trigger1,再由Trigger1去调用State1::Action1。之所以整个调用过程做得如此复杂,原因在于State1知道怎么做,但是不知道做哪些;而Trigger1知道做哪些,却不知道怎么做。简言之,在运行时要做哪个动作是由State1和Trigger1两个对象同时决定的,缺一不可。而在C++中一个消息只能同时发给一个对象,在程序需要发消息给多个对象的时候,只能一个一个地去遍历。Lisp有可以把一个消息同时发给多个对象的能力,这种能力就叫做multiple dispatch。
上面的示例代码也可以叫做double dispatch,在wiki上有详细的解释。
参考资料:
- Multiple Dispatch
- Multiple Dispatch Wiki