ファクトリモードの代替方法

3723 ワード

前言
オープンクローズの原則は、プログラムの拡張がオープンであることを要求し、プログラムの修正はクローズである.開放閉鎖を実現する原則は通常、具体的なプログラミングではなく抽象プログラミングに対して行われる.抽象は相対的に安定しているため、拡張された開放は抽象体を継承し、その方法を書き換えることで、新しい方法の拡張を実現する.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文が現れる可能性があります.厳しいことに、これは開放閉鎖の原則に反して、新しい需要の発生であり、理論的には1つのサブクラスだけを拡張すればよいが、case文を追加する必要はなく、修正はアーキテクチャの不安定さを意味する.
インプリメンテーション
1つの実行可能な方法は、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を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<

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」の役割は,c++mangle過symbolではなく,soにおけるgetInstance symbolを生成するC言語のsymbol命名規則である.
最後に、顧客階層のコードを見てください.cpp
#include 
#include "Procedure.h"

using namespace std;

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

main関数はパラメータを受け入れ,パラメータはサブクラスのモジュール名(TestA,TestB...)である.
レビュー
このようにすることで、拡張は拡張だけであり、抽象クラスを継承し、インタフェースを実現し、soにカプセル化するmain.cpp、Procedure.cppは安定しており,外部に露出しているのは抽象的なインタフェース,すなわちTestInterface抽象クラス,libの名前であり,switch caseの存在を解消している.