strUCt Socket { // base class virtual void OnRecv(); }; stuct MySocket { // your event-handle class virtual void OnRecv() { /* do sth here ... */ } } |
疑问:很多时候这样做实在很烦,特别是做小程序的时候,或者需要快速做原型的时候,一眼望去小小的程序一上来就继承了一大堆东西,颇为不爽。只是想着能省事一点,希望能像那些脚本语言一样快速绑定消息响应,而不是以继承开始工作——我已经害怕看到长长的类继承树了,很多时候根本不必要继承整个类;又或者某些类只提供一个接口而不是具体的类又或者需要多重继承,处理都有一定麻烦;最麻烦的莫过于有时候需要改变响应处理,难道继承好几个下来么——这么多虚表也是浪费啊。
点评:为了使用Socket就必须继承Socket,这可以说是Socket的设计的问题。如果需要实现类似的功能的话,可以写成如下,虽然和继承 Socket 没有多少本质的差别,不过确实把消息处理类和Socket的实现扯开了。
struct SocketEventHandler { virtual void OnRecv() { /* ... */ } virtual void OnSend() { /* ... */ } }; struct Socket { void set_handler( SocketEventHandler* h ) { handler_ = h; } private: SocketEventHandler* handler_; }; struct MyHandler : SocketEventHandler { void OnRecv() { ... } }; Socket s; MyHandler h; s.set_handler( &h ); |
struct Socket { void OnRecv() { if(OnRecvHandle!=NULL) OnRecvHandle(); } void (*OnRecvHandle) (); }; |
void MyOnRecv(); // your event-handle function Socket foo; foo.OnRecvHandle = MyOnRecv; |
我觉得委托最本质的是提供一种类型安全的动态消息响应转移机制。
以前,我对委托一无所知,我觉得无非就是一个类型安全的智能指针,而所谓的Multi-Cast Delegation无非就是一个智能指针数祖……是不是还有Any-Cast Delegation呢?我不知道,也许有吧,无非就是智能指针数祖+随机数发生器……
但是,实际上并不是那么简单。你可以把我刚才说的函数指针封装一下弄一个类封装起来,不过,这直接导致某个消息的响应只能是固定死的函数指针类型,甚至不能是可爱的Functor或者是某个类的成员函数。你可能会跟我抬杠说这怎么可能,不是可以用template实现么?我们来看一个例子
假设某个委托类 Dummy_Delegation 拥有一个成员函数用来连接处理函数 template<class T> void Dummy_Delegation::Connect(T _F); 没错,_F可以不一定函数指针,也可以是Functor,我们利用_F()来呼叫响应函数,一切看起来是多么美好——但是,很不幸,这个_F无法保存下来供消息产生的时候呼叫……
一切都因为这个该死的template<class T>,你无法在Dummy_Delegation内定义一个T类型的变量或者指针来保存_F。退一万步说,你把T作为整个Dummy的模版,还是避免不了在模版实例化的时候定死类型。于是,整个Delegation的通用性大打折扣……
实际上,我们希望有这么一种Delegation,他可以把消息响应动态绑定到任何一个类的成员函数上只要函数类型一致。注意,这里说的是任何一个类。这就要求我们屏蔽信号发生器和响应类之间的耦合关系,即,让他们相互都不知道对方是谁甚至不知道对方的类型信息。
这个方法可行么?Yes!
桥式委托(Bridge Delegation) ---- 利用泛型+多态来实现
请允许我杜撰一个名词:桥式委托(Bridge Delegation)
实现这么一个东西真的很有意思,其实,像gtk+/qt很多需要"信号/反馈"(signal/slot)的系统都是这么实现的。
说到GP和Template,那真的可以算是百家争鸣了,就像boost和loki还在争夺新的C++标准智能指针的地位打得不可开交。而Functor这个东西有是很多GP algo的基础,比如sort/for_each等等。
整个桥式委托的结构如下图:
Signal <>-------->* Interface
^
Implementation<Receiver> -------------> Receiver
我们搭建了一个Interface/Implementation的桥用来连接Singal和Receiver,这样就可以有效隔开双方的直接耦合。用之前我们的Socket类来演示如下: