今天在看文章的時候遇到了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