前言
开放封闭原则,要求程序的扩展是开放的,程序的修改是封闭的。做到开放封闭的原则通常是针对抽象编程而不是具体编程,因为抽象是相对稳定的,扩展的开放则通过继承抽象体,覆写其方法,实现新方法的扩展。在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的存在。