Permanent Link: http://wutiam.net/2009/03/use-factory-pattern-to-solve-problem/
我前段时间去面试时的一道题目,问题如下:
有一个脚本文件,每行有一句指令或空行,指令格式:
Command[,Param[,Value]]其中Parameter 和 Value 非必须。设计一套解析指令的类,高效且易于扩展(尽可能降低代码内部耦合性)。
当时虽然都想到了,不过满脑混沌,没有完整明白地表达出来。本来想去公司再看看代码是怎么实现的,昨天在网上闲逛的时候忽然看到了这篇笔记,那就顺便也整理了下自己的思路,结合实际温故理论。
假设有“移动(Move)”、“攻击(Attack)”等几个指令;建立一个工厂类,并将所有指令类预先注册到工厂中,由工厂调用每个指令类的静态成员函数 CreateInstance() 来实现指令类实例的创建:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | class Command { public: virtual ~Command(void) {} virtual void Excute(void) = 0; protected: Command(const std::string& strParam, const std::string& strValue) : m_strParam(strParam) , m_strValue(strValue) { } private: // this constructor is prohibited Command(void) {} private: std::string m_strParam; std::string m_strValue; }; class CmdMove : public Command { public: // static member function used by factory static Command* CreateInstance(const std::string& strParam, const std::string& strValue) { return (Command*)new CmdMove(strParam, strValue); } virtual ~CmdMove(void) {} virtual void Excute(void) { // TODO } protected: CmdMove(const std::string& strParam, const std::string& strValue) : Command(strParam, strValue) { // TODO } private: CmdMove(void) {} }; class CmdAttack : public Command { public: static Command* CreateInstance(const std::string& strParam, const std::string& strValue) { return (Command*)new CmdAttack(strParam, strValue); } virtual ~CmdAttack(void) {} virtual void Excute(void) { // TODO } protected: CmdAttack(const std::string& strParam, const std::string& strValue) : Command(strParam, strValue) { // TODO } private: CmdAttack(void) {} }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | // the format of the function used by factory typedef Command* (*CreateCmdInstanceFunc)(const std::string&); class CommandFactory { public: static CommandFactory* GetInstance(void) { static CommandFactory cfInstance; return &cfInstance; } virtual ~CommandFactory(void) {} void RegCommand(std::string& strCmdName, CreateCmdInstanceFunc func) { m_mapCommands[strCmdName] = func; } void ExcuteCommand(const std::string& strCmdName, const std::string& strParam, const std::string& strValue) { Command* pCmd = m_mapCommands[strCmdName](strParam, strValue); pCmd->Excute(); delete pCmd; pCmd = NULL; } private: CommandFactory(void) {} private: std::map m_mapCommands; }; |
由于工厂类同时使用了单例模式(Singleton),在应用程序的预处理阶段通过调用:
1 | CommandFactory::GetInstance()->RegCommand(std::string("MOVE"), CmdMove::CreateInstance); |
便可完成指令的注册。在脚本文件解析阶段通过调用:
1 | CommandFactory::GetInstance()->ExcuteCommand(strCmdName, strParam, strValue); |
便可完成对应指令的操作。
以上代码不包含容错处理,不足在于所有 Command 的子类都需要约定定义一个静态类成员函数(CreateInstance())用于该指令实例的创建,不知是否还有更完美的解决方案?

Dbger
好文章!
注册命令可以利用一个静态变量构造函数来做:
class CmdRegister
{
public:
CmdRegister(const string& cmdName, CreateCmdInstanceFunc createFunc)
{
CommandFactory::GetInstance()->RegCommand(cmdName, createFunc);
}
}
#define REG_CMD(arg1, arg2) \
static CmdRegister _cmdRegister(arg1, arg2);
当然,这个其实还是在运行时做的。
islet8
@ Dbger
嗯,由每个子类自己向工程注册,比由工厂统一为各子类注册更灵活一些,不过还是需要和子类约定“必须调一次注册”的规则,尤其是当代码要提供给第三方使用时