在实现 MessageFactory 类来实例化 Message 对象时,我使用了类似的东西:
class MessageFactory
{
public:
static Message *create(int type)
{
switch(type) {
case PING_MSG:
return new PingMessage();
case PONG_MSG:
return new PongMessage();
....
}
}
这工作正常,但每次我添加新消息时,我都必须添加一个新的 XXX_MSG 并修改 switch 语句。
经过一番研究,我找到了一种在编译时动态更新 MessageFactory 的方法,这样我就可以添加任意数量的消息,而无需修改 MessageFactory 本身。这允许更清洁和更容易维护代码,因为我不需要修改三个不同的位置来添加/删除消息类:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
class Message
{
protected:
inline Message() {};
public:
inline virtual ~Message() { }
inline int getMessageType() const { return m_type; }
virtual void say() = 0;
protected:
uint16_t m_type;
};
template<int TYPE, typename IMPL>
class MessageTmpl: public Message
{
enum { _MESSAGE_ID = TYPE };
public:
static Message* Create() { return new IMPL(); }
static const uint16_t MESSAGE_ID; // for registration
protected:
MessageTmpl() { m_type = MESSAGE_ID; } //use parameter to instanciate template
};
typedef Message* (*t_pfFactory)();
class MessageFactory⋅
{
public:
static uint16_t Register(uint16_t msgid, t_pfFactory factoryMethod)
{
printf("Registering constructor for msg id %d\n", msgid);
m_List[msgid] = factoryMethod;
return msgid;
}
static Message *Create(uint16_t msgid)
{
return m_List[msgid]();
}
static t_pfFactory m_List[65536];
};
template <int TYPE, typename IMPL>
const uint16_t MessageTmpl<TYPE, IMPL >::MESSAGE_ID = MessageFactory::Register(
MessageTmpl<TYPE, IMPL >::_MESSAGE_ID, &MessageTmpl<TYPE, IMPL >::Create);
class PingMessage: public MessageTmpl < 10, PingMessage >
{⋅
public:
PingMessage() {}
virtual void say() { printf("Ping\n"); }
};
class PongMessage: public MessageTmpl < 11, PongMessage >
{⋅
public:
PongMessage() {}
virtual void say() { printf("Pong\n"); }
};
t_pfFactory MessageFactory::m_List[65536];
int main(int argc, char **argv)
{
Message *msg1;
Message *msg2;
msg1 = MessageFactory::Create(10);
msg1->say();
msg2 = MessageFactory::Create(11);
msg2->say();
delete msg1;
delete msg2;
return 0;
}
这里的模板通过将所有新的 Message 类(例如 PingMessage 和 PongMessage)注册到 MessageFactory 类来实现魔术,这些类都是 MessageTmpl 的子类。
这很好用并且简化了代码维护,但我仍然对这种技术有一些疑问:
这是一种已知的技术/模式吗?是什么名字?我想搜索更多关于它的信息。
我想将用于存储新构造函数的数组MessageFactory::m_List[65536] 设为 std::map,但这样做会导致程序在到达 main() 之前出现段错误。创建一个包含 65536 个元素的数组太过分了,但我还没有找到一种方法来使它成为一个动态容器。
对于作为 MessageTmpl 子类的所有消息类,我必须实现构造函数。如果不是,它将不会在 MessageFactory 中注册。
例如注释 PongMessage 的构造函数:
class PongMessage: public MessageTmpl < 11, PongMessage > { public: //PongMessage() {} /* HERE */ virtual void say() { printf("Pong\n"); } };
将导致 MessageFactory 未注册 PongMessage 类,并且程序将在MessageFactory::Create(11)行中出现段错误。问题是
为什么课程不会注册?必须添加我需要的 100 多条消息的空实现感觉低效且不必要。