本文展示了实际项目中使用到的一个工厂模式实现,在向系统中添加新类型时,只需要在新类型的实现文件这一处做改动,将新增类型对应用程序代码的干扰降到了最低。
这个工厂实现的基本思想是:继承自同一个接口的新类型,通过一个函数来创建其对象,利用C++ 中类的构造函数会被自动调用这一特性,在新类型的实现文件中定义一个静态的(辅助)类对象,在该辅助类的构造函数中,向工厂单例注册新类型的创建函数。
先看下代码,然后我们一一来解释。下面是命令接口 CommandObject 的头文件内容:
class CommandObject
{
public:
CommandObject(){}
virtual ~CommandObject(){}
virtual void execute() = 0;
};
CommandObject 是一个纯虚类,作为公共的接口。
我在正式的系统中使用命令模式,封装特定的操作,传递命令对象给一些 UI 元素,如 button 等,在 UI 元素被鼠标或按键触发时,会调用关联的 CommandObject 来执行特定的命令。有关命令模式,参考文章《设计模式介绍之三:命令模式(command)》。
下面是命令对象工厂类的头文件:
#ifndef COMMANDOBJECTFACTORY_H
#define COMMANDOBJECTFACTORY_H
#include "commandObject.h"
typedef CommandObject * (*LPFNCREATE)();
class CommandObjectFactory
{
CommandObjectFactory(const CommandObjectFactory &);
CommandObjectFactory & operator=(const CommandObjectFactory &);
CommandObjectFactory();
public:
~CommandObjectFactory();
static CommandObjectFactory * instance();
CommandObject * commandObject(const char * szKeyword);
void regist(const char * szKeyword, LPFNCREATE lpfnCreate);
private:
const char ** m_keywords;
LPFNCREATE * m_functions;
int m_iCount;
int m_iCursor;
};
#define EXPORT_COMMAND_CREATOR(KEYWORD, COMMANDCLASS) \
CommandObject * _command_object_creator_##KEYWORD() {\
return new COMMANDCLASS;\
}\
class Static##KEYWORD##PluginInstance{ \
public: \
Static##KEYWORD##PluginInstance(){ \
CommandObjectFactory::instance()->regist(#KEYWORD, _command_object_creator_##KEYWORD);\
}\
};\
static Static##KEYWORD##PluginInstance static##KEYWORD##Instance
#endif // COMMANDOBJECTFACTORY_H
在这个头文件中,定义了 CommandObjectFactory 这个工厂类。首先它是一个单例( singleton ),这是通常的做法,工厂类作为单例实现。关于单例,请参考文章《设计模式介绍之二:单例模式(Singleton)》。
CommandObjectFactory 定义了用于创建对象的工厂方法 commandObject ,它接受一个字符串作为关键字,内部根据这个关键字来创建命令对象。还定义了一个方法 regist ,用来向工厂内注册命令对象的创建函数,主要是被后面定义的辅助宏 EXPORT_COMMAND_CREATOR 使用,自动进行创建函数的注册。
宏 EXPORT_COMMAND_CREATOR 有两个参数,一个是与具体命令对象实现类一一对应的关键字 KEYWORD,一个是命令对象类类名 COMMANDCLASS 。这个宏非常关键,正是它帮助我们完成创建函数的注册,同时使得我们把新增类型的代码改动限制在新类型的实现文件中,对已有代码没有任何影响。
宏 EXPORT_COMMAND_CREATOR 展开后又分为几部分:辅助类声明、作用域为文件的全局静态辅助类实例、辅助类构造函数调用 CommandObjectFactory::regist() 注册创建函数。它的使用也非常简单,我们会在后面提到。
下面是 CommandObjectFactory 的实现:
#include "commandObjectFactory.h" #include#include #include using namespace std; #define INITIALISE_SIZE 32 #define INCREMENT_SIZE 8 static CommandObjectFactory * s_instance = 0; CommandObjectFactory::CommandObjectFactory() : m_keywords(0) , m_functions(0) , m_iCount(0) , m_iCursor(0) { } CommandObjectFactory::~CommandObjectFactory() { } CommandObjectFactory * CommandObjectFactory::instance() { if(!s_instance) { s_instance = new CommandObjectFactory; cout << "CommandObjectFactory initialised" << endl; } return s_instance; } void CommandObjectFactory::regist(const char * szKeyword, LPFNCREATE lpfnCreate) { if(!szKeyword || !lpfnCreate) return; //repeat check for(int i = 0; i < m_iCursor; ++i) { if(!strcmp(m_keywords[i], szKeyword)) return ; } if(!m_functions) { m_functions = (LPFNCREATE*)calloc(INITIALISE_SIZE, sizeof(LPFNCREATE)); m_keywords = (const char**)calloc(INITIALISE_SIZE, sizeof(char*)); m_iCount = INITIALISE_SIZE; m_iCursor = 0; } else if( m_iCursor == m_iCount ) { m_iCount += INCREMENT_SIZE; m_functions = (LPFNCREATE*)realloc( m_functions, m_iCount * sizeof(LPFNCREATE) ); m_keywords = (const char**)realloc( m_keywords, m_iCount * sizeof(char*)); } m_keywords[m_iCursor] = (const char *)strdup(szKeyword); m_functions[m_iCursor] = lpfnCreate; m_iCursor++; cout << "register create function for - " << szKeyword << endl; } CommandObject * CommandObjectFactory::commandObject(const char * szKeyword) { for(int i = 0; i < m_iCursor; ++i) { if(!strcmp(m_keywords[i], szKeyword)) { return m_functions[i](); } } cout << "no create function for - " << szKeyword << endl; return 0; }
实现比较简单,我们在 CommandObjectFactory 内部维护了两个数组,分别存贮关键字和命令对象创建函数,两者一一对应, regist() 函数维护创建函数的注册和内部数组的动态增长。 commandObject() 函数则根据传入的关键字 szKeyword ,在内部的数组中做字符串比较,关键字匹配后定位对应的创建函数来创建命令对象。
下面看看具体命令对象类的实现和自动注册宏 EXPORT_COMMAND_CREATOR 的使用。代码:
class ShutdownCommand : public CommandObject
{
public:
void execute()
{
cout << endl << "ShutdownCommand::execute" << endl;
}
};
EXPORT_COMMAND_CREATOR(shutdown, ShutdownCommand);
class RebootCommand : public CommandObject
{
public:
void execute()
{
cout << endl << "RebootCommand::execute" << endl;
}
};
EXPORT_COMMAND_CREATOR(reboot, RebootCommand);
一切都很直观,不必多说了。
下面是 main() 函数,看看怎么使用命令对象工厂来创建想要的命令对象:
int main()
{
CommandObject * cmd = CommandObjectFactory::instance()->commandObject("shutdown");
cmd->execute();
return 0;
}
非常简单,不必要解释了。下面是程序执行的结果:

好啦,到现在为止,一个简单好用的简单工厂模式实现介绍完毕,我特意做了简