设计模式介绍之六:工厂模式(factory)的巧妙实现

    本文展示了实际项目中使用到的一个工厂模式实现,在向系统中添加新类型时,只需要在新类型的实现文件这一处做改动,将新增类型对应用程序代码的干扰降到了最低。

    这个工厂实现的基本思想是:继承自同一个接口的新类型,通过一个函数来创建其对象,利用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 <iostream>
#include <string.h>
#include <malloc.h>

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;
}

    非常简单,不必要解释了。下面是程序执行的结果:

设计模式介绍之六:工厂模式(factory)的巧妙实现

    好啦,到现在为止,一个简单好用的简单工厂模式实现介绍完毕,我特意做了简化,以便能更好的理解实现的思路,在实际的项目中,稍微复杂了一些。

    回顾:


设计模式介绍之六:工厂模式(factory)的巧妙实现

上一篇:linux下如何删除十字符libudev.so病毒文件


下一篇:菜根谭#59