2

編集: 回答のコメントに続くマイナーな修正 (仮想印刷; mpInstance を返す)。

任意の基本クラスから子クラスを派生できるシステムを作成しようとしています。その実装は、基本クラスの実装を置き換える必要があります。

基本クラス オブジェクトを作成および使用するすべてのオブジェクトは、オブジェクトの作成または呼び出しの方法を変更してはなりません。つまり、実際に Child クラスを作成するときでも、BaseClass.Create() を呼び出し続ける必要があります。基本クラスは、オーバーライドできることを認識していますが、それらをオーバーライドする具象クラスは認識していません。

そして、すべての Child クラスの登録を 1 か所で行うようにしたいと考えています。

これが私の実装です:

class CAbstractFactory
{
public:
    virtual ~CAbstractFactory()=0;
};

template<typename Class>
class CRegisteredClassFactory: public CAbstractFactory
{
public:
    ~CRegisteredClassFactory(){};
    Class* CreateAndGet()
    {
        pClass = new Class;
        return pClass;
    }
private:
    Class* pClass;
};

// holds info about all the classes that were registered to be overridden
class CRegisteredClasses
{
public:
    bool find(const string & sClassName);
    CAbstractFactory* GetFactory(const string & sClassName)
    {
        return mRegisteredClasses[sClassName];
    }
    void RegisterClass(const string & sClassName, CAbstractFactory* pConcreteFactory);
private:
    map<string, CAbstractFactory* > mRegisteredClasses;

};


// Here I hold the data about all the registered classes. I hold statically one object of this class.
// in this example I register a class CChildClass, which will override the implementation of CBaseClass, 
// and a class CFooChildClass which will override CFooBaseClass
class RegistrationData
{
    public:
        void RegisterAll()
        {
            mRegisteredClasses.RegisterClass("CBaseClass", & mChildClassFactory);
            mRegisteredClasses.RegisterClass("CFooBaseClass", & mFooChildClassFactory);
        };
        CRegisteredClasses* GetRegisteredClasses(){return &mRegisteredClasses;};
    private:    
        CRegisteredClasses mRegisteredClasses;
        CRegisteredClassFactory<CChildClass> mChildClassFactory;
        CRegisteredClassFactory<CFooChildClass> mFooChildClassFactory;
};

static RegistrationData StaticRegistrationData;

// and here are the base class and the child class 
// in the implementation of CBaseClass::Create I check, whether it should be overridden by another class.
class CBaseClass
{
public:
    static CBaseClass* Create()
    {
        CRegisteredClasses* pRegisteredClasses = StaticRegistrationData.GetRegisteredClasses();
        if (pRegisteredClasses->find("CBaseClass"))
        {
            CRegisteredClassFactory<CBaseClass>* pFac = 
                dynamic_cast<CRegisteredClassFactory<CBaseClass>* >(pRegisteredClasses->GetFactory("CBaseClass"));

            mpInstance = pFac->CreateAndGet();
        }
        else
        {
            mpInstance = new CBaseClass;
        }
        return mpInstance;
    }
    virtual void Print(){cout << "Base" << endl;};
private:
    static CBaseClass* mpInstance;

};

class CChildClass : public CBaseClass
{
public:
    void Print(){cout << "Child" << endl;};
private:

};

この実装を使用して、他のクラスからこれを行っている場合:

StaticRegistrationData.RegisterAll();
CBaseClass* b = CBaseClass::Create();
b.Print();

出力に「Child」が含まれることを期待しています。

このデザインどう思いますか?物事を複雑にしすぎたので、もっと簡単にできるでしょうか? また、抽象クラスから継承したテンプレートを作成してもよろしいですか?

dynamic_pointer を使用する必要がありました (それ以外の場合はコンパイルしませんでした) - 何かが間違っているというヒントですか?

ありがとうございました。

4

5 に答える 5

2

この種のパターンはかなり一般的です。私は C++ の専門家ではありませんが、Java ではこれがどこにでも見られます。コンパイラは、マップに格納されているファクトリの種類を認識できないため、動的キャストが必要なようです。私の知る限り、現在の設計でそれについてできることはあまりありません。これらのオブジェクトの使用方法を知っておくと役立ちます。Java のデータベース ライブラリ (JDBC) で同様のタスクを実行する方法の例を挙げましょう。

システムには、JDBC ドライバーを認識する DriverManager があります。何らかの方法でドライバーを登録する必要があります (詳細は重要ではありません)。データベース接続を要求するたびに登録されると、Connection オブジェクトが取得されます。通常、このオブジェクトは OracleConnection または MSSQLConnection などになりますが、クライアント コードは「接続」しか認識しません。Statement オブジェクトを取得するには、connection.prepareStatement と言い、PreparedStatement 型のオブジェクトを返します。ただし、実際には OraclePreparedStatement または MSSQLPreparedStatement です。Statements のファクトリは Connection にあり、Connections のファクトリは DriverManager にあるため、これはクライアントに対して透過的です。

クラスが同様に関連している場合、DriverManager の getConnection メソッドが Connection を返すのと同じように、特定のタイプのクラスを返す関数が必要になる場合があります。キャストは不要です。

考慮すべきもう 1 つのアプローチは、必要な特定のクラスごとに factory-method を持つ factory を使用することです。次に、Factory のインスタンスを取得するために必要な factory-factory は 1 つだけです。サンプル (これが適切な C++ でない場合は申し訳ありません):

class CClassFactory
{
  public:
    virtual CBaseClass* CreateBase() { return new CBaseClass(); }
    virtual CFooBaseClass* CreateFoo() { return new CFooBaseClass();}
}

class CAImplClassFactory : public CClassFactory
{
  public:
    virtual CBaseClass* CreateBase() { return new CAImplBaseClass(); }
    virtual CFooBaseClass* CreateFoo() { return new CAImplFooBaseClass();}
}

class CBImplClassFactory : public CClassFactory // only overrides one method
{
  public:
    virtual CBaseClass* CreateBase() { return new CBImplBaseClass(); }
}

継承の使用を批判する他のコメントについては、私の意見では、インターフェイスとパブリック継承の間に違いはありません。ですから、理にかなっているところでは、インターフェイスの代わりにクラスを使用してください。純粋なインターフェイスは、長期的にはより柔軟になるかもしれませんが、そうではないかもしれません. クラス階層に関する詳細がなければ、何とも言えません。

于 2008-12-23T14:29:17.897 に答える
1

通常、基底クラス/派生クラス パターンは、基底クラスにインターフェイスがあり、そのインターフェイスが派生クラスに実装されている場合 (IS-A 関係) に使用されます。あなたの場合、基本クラスは派生クラスとは何の関係もないようです - void* である可能性もあります。

基本クラスと派生クラスの間に接続がない場合、なぜ継承を使用するのですか? 工場の生産物を一般的な方法で使用できない場合、工場を持つ利点は何ですか? あなたが持っている

class CAbstractFactory
{
public:
    virtual ~CAbstractFactory()=0;
};

これは完全に間違っています。工場は、すぐに使用できるものを製造する必要があります。

class CAbstractFactory
{
public:
    virtual ~CAbstractFactory(){};
public:
    CBaseClass* CreateAndGet()
    {
        pClass = new Class;
        return pClass;
    }
private:
    CBaseClass* pClass;

protected:
    CBaseClass *create() = 0;

};

一般に、継承、仮想関数、およびテンプレートを混合してはならない方法で混合しています。

于 2008-12-23T14:49:46.390 に答える
0

私が間違っているかもしれませんが、あなたの CBaseClass::Create() メソッドに return ステートメントが見つかりませんでした!

于 2008-12-23T14:06:45.883 に答える
0

すべてのコードを読んだり、詳細を調べたりすることなく、次のことを行う必要があったようです。

  • タイプCChildClassのmake b
  • CBaseClass::Print仮想関数 を作成します。
于 2008-12-23T13:40:48.867 に答える
0

個人的には、この設計は継承を使いすぎていると思います。

「任意の基本クラスから子クラスを派生できるシステムを作成しようとしています。その実装は、基本クラスの実装を置き換える必要があります。」- IS-A 関係がそれほど柔軟であるべきだとは知りません。

インターフェイス (C++ の純粋仮想クラス) と mixin の動作を使用したほうがよいのではないでしょうか。Java で書いていたら、次のようにします。

public interface Foo
{
    void doSomething();
}

public class MixinDemo implements Foo
{
    private Foo mixin;

    public MixinDemo(Foo f)
    {
        this.mixin = f;
    }

    public void doSomething() { this.mixin.doSomething(); }
}

MixinDemo に渡す Foo 実装を変更することで、必要に応じて動作を変更できるようになりました。

于 2008-12-23T14:21:54.433 に答える