替代工厂模式的方法

前言

开放封闭原则,要求程序的扩展是开放的,程序的修改是封闭的。做到开放封闭的原则通常是针对抽象编程而不是具体编程,因为抽象是相对稳定的,扩展的开放则通过继承抽象体,覆写其方法,实现新方法的扩展。在c++中,无论是通过工厂模式,还是其他,似乎免不了switch case的处理,因为总会有创建具体子类的地方,代码大概会是长这个样子的:

ParentClass * ClassFactory(int classType)
{
    switch(classType)
    {
    case 1:
        ParentClass * p = new SubClass1();
        break;
    case 2:
        ParentClass * p = new SubClass2();
        break;
    ...
    }

    return p;
}

当代码里出现类似于这样的switch case时,会让人比较难于忍受,特别是当需求不断增多,扩展很多的时候,可能会出现几十处case语句。苛刻的讲,这是违背开放封闭原则的,一种新需求的产生,理论上只扩展一个子类即可,而不需要另加一条case语句,修改意味着架构的不稳定。

实现

一种可行的方法是借助于so部署。具体做法如下:
我们可能有一个模块,这个模块会针对客户的需求扩展,这时很容易想到通过抽象类的方式对这个模块的业务做抽象,一个抽象类这样产生了:

//该内容在TestInterface.h中
class TestInterface
{
public:
    TestInterface(){};
    virtual ~TestInterface(){};
    virtual void InterfacePrint() = 0;
};

typedef TestInterface* (*CallFunc)(void);

在这里简化流程,InterfacePrint作为这个模块抽象之后对外呈现的行为,TestA是一个实现了InterfacePrint的子类,
TestA.h内容如下:

#include "TestInterface.h"

class TestA:public TestInterface
{
public:
    TestA();
    ~TestA();
    void InterfacePrint();
};

extern "C"
{
TestInterface * getInstance();
}

TestA.cpp内容如下:

#include "TestA.h"
#include 
using namespace std;

TestA::TestA()
{
    cout<<"TestA Construct"<

把TestA.cpp编译成一个独立的so文件:

g++ -c  TestA.cpp
g++ -fPIC -shared TestA.o -o libTestA.so

新的需求来了,我们用TestB来做扩展:
TestB.h内容如下:

#include "TestInterface.h"

class TestB:public TestInterface
{
public:
    TestB();
    ~TestB();
    void InterfacePrint();
};

extern "C"
{
TestInterface * getInstance();
}

TestB.cpp内容如下:

#include "TestB.h"
#include 
using namespace std;

TestB::TestB()
{
    cout<<"TestB Construct"<

生成libTestB.so,

g++ -c  TestB.cpp
g++ -fPIC -shared TestB.o -o libTestB.so

在这里用Procedure.cpp作为middle ware处理一些逻辑,
Procedure.h内容如下:

#include "TestInterface.h"

TestInterface * OpenInterface(char * pszModuleName);

Procedure.cpp内容如下:

#include "TestInterface.h"
#include 
#include 
#include 
using namespace std;

TestInterface * OpenInterface(char * pszModuleName)
{
    char szLibName[64] = {0};
    sprintf(szLibName,"./lib%s.so",pszModuleName);
    cout<

OpenInterface()函数的逻辑还算简单,参数用于组成具体的so文件名,通过dlopen,dlsym拿到so库文件里的getInstance函数symbol,然后通过symbol获取到具体子类的对象。前面TestA.cpp\TestB.cpp中extern "C"的作用在于生成在so里的getInstance symbol是C语言的symbol命名规则,而不是c++ mangle过的symbol。

最后看下客户层次的代码,main.cpp

#include 
#include "Procedure.h"

using namespace std;

int main(int argc, char** argv)
{
    if(argc < 2)
    {
        cout<<"argv error"<InterfacePrint();
    delete p;
    return 0;
}

main函数接受参数,参数即为子类的模块名字(TestA,TestB...)

回顾

通过这种方式,扩展就只是扩展,继承抽象类,实现接口,封装在so中,main.cpp、Procedure.cpp是稳定的,对外暴露的是抽象的接口,即TestInterface抽象类,还有lib的名字,从而消除了switch case的存在。

你可能感兴趣的:(替代工厂模式的方法)