下面我们将介绍另一种实现消息映射的方式:职责链模式
#include <iostream>
#include <map>
#include <cassert>
#define PRINT( msg ) { std::cout << msg << std::endl; }
#define FUNC( func ) void func() { PRINT( #func##"()" ); }
#define MFUNC( class, func ) void func() { PRINT( #class##"::"###func##"()" ); }
#define VMFUNC( class, func ) virtual MFUNC( class, func )
enum MsgID {
MsgID_1,
MsgID_2,
};
namespace COR { // chain of responsibility
class Wnd;
typedef void (Wnd::*MemberFunc)();
class Wnd {
public:
virtual void HandleMsg( int id ) {
switch ( id ) {
case MsgID_1: Handle1(); break;
case MsgID_2: Handle2(); break;
}
}
private:
MFUNC( Wnd, Handle1 );
MFUNC( Wnd, Handle2 );
};
class MyWnd : public Wnd {
public:
virtual void HandleMsg( int id ) {
switch( id ) {
case MsgID_1: Handle1(); break;
default: Wnd::HandleMsg( id );
}
}
private:
MFUNC( MyWnd, Handle1 );
};
}
namespace AutoTest {
void TestCORMsgMap() {
using namespace COR;
Wnd* wnd = new MyWnd;
wnd->HandleMsg( MsgID_1 );
wnd->HandleMsg( MsgID_2 );
}
}
void main() {
AutoTest::TestCORMsgMap();
}
原理
不需要消息映射表,而是利用虚函数HandleMsg()向父类递归查找,详情可参考《设计模式》职责链模式。
细节
1. 可以把switch...case用查找表替换掉,但绝非必要。通常使用此法是在有封装之必要时才会采取的措施,详情可参考
http://blog.csdn.net/hjsunj/archive/2008/01/07/2028597.aspx
http://blog.csdn.net/hjsunj/archive/2008/01/11/2037354.aspx
2. 使用NVI模式(《Effective C++ 3rd》chapter35)让思路更加清晰,此时需借助查找表
将HandleMsg变为非虚函数:
void HandleMsg( int id ) {
MemberFunc func = Lookup( id );
if ( func != NULL ) {
(this->*func)();
}
}
virtual MemberFunc Lookup( int id ) {
// 如果thisclass有id之处理函数,返回之
// 否则返回BaseClass::Lookup( id );
}
优缺点
1. 实现简单:不再需要定制宏,不再需要建立消息映射表,所需的只是重写虚函数。
2. 节省内存:由于不再需要为每个派生类准备查找表,节省内存是自然而然的事。
2. 效率低下: 通常使用虚函数的效率会高于查找表(《设计模式》p204),但那只对一次调用成立。职责链模式对虚函数递归调用,其时间开销必然要高于映射表方案的迭代查找。
#include <iostream>
#include <map>
#include <cassert>
#define PRINT( msg ) { std::cout << msg << std::endl; }
#define FUNC( func ) void func() { PRINT( #func##"()" ); }
#define MFUNC( class, func ) void func() { PRINT( #class##"::"###func##"()" ); }
#define VMFUNC( class, func ) virtual MFUNC( class, func )
enum MsgID {
MsgID_1,
MsgID_2,
};
namespace COR { // chain of responsibility
class Wnd;
typedef void (Wnd::*MemberFunc)();
class Wnd {
public:
virtual void HandleMsg( int id ) {
switch ( id ) {
case MsgID_1: Handle1(); break;
case MsgID_2: Handle2(); break;
}
}
private:
MFUNC( Wnd, Handle1 );
MFUNC( Wnd, Handle2 );
};
class MyWnd : public Wnd {
public:
virtual void HandleMsg( int id ) {
switch( id ) {
case MsgID_1: Handle1(); break;
default: Wnd::HandleMsg( id );
}
}
private:
MFUNC( MyWnd, Handle1 );
};
}
namespace AutoTest {
void TestCORMsgMap() {
using namespace COR;
Wnd* wnd = new MyWnd;
wnd->HandleMsg( MsgID_1 );
wnd->HandleMsg( MsgID_2 );
}
}
void main() {
AutoTest::TestCORMsgMap();
}
原理
不需要消息映射表,而是利用虚函数HandleMsg()向父类递归查找,详情可参考《设计模式》职责链模式。
细节
1. 可以把switch...case用查找表替换掉,但绝非必要。通常使用此法是在有封装之必要时才会采取的措施,详情可参考
http://blog.csdn.net/hjsunj/archive/2008/01/07/2028597.aspx
http://blog.csdn.net/hjsunj/archive/2008/01/11/2037354.aspx
2. 使用NVI模式(《Effective C++ 3rd》chapter35)让思路更加清晰,此时需借助查找表
将HandleMsg变为非虚函数:
void HandleMsg( int id ) {
MemberFunc func = Lookup( id );
if ( func != NULL ) {
(this->*func)();
}
}
virtual MemberFunc Lookup( int id ) {
// 如果thisclass有id之处理函数,返回之
// 否则返回BaseClass::Lookup( id );
}
优缺点
1. 实现简单:不再需要定制宏,不再需要建立消息映射表,所需的只是重写虚函数。
2. 节省内存:由于不再需要为每个派生类准备查找表,节省内存是自然而然的事。
2. 效率低下: 通常使用虚函数的效率会高于查找表(《设计模式》p204),但那只对一次调用成立。职责链模式对虚函数递归调用,其时间开销必然要高于映射表方案的迭代查找。