13

私のアプリケーションには、1 回インスタンス化される 10 ~ 20 個のクラスがあります[*]。次に例を示します。

class SomeOtherManager;

class SomeManagerClass {
public:
    SomeManagerClass(SomeOtherManager*);
    virtual void someMethod1();
    virtual void someMethod2();
};

クラスのインスタンスは、1 つのオブジェクトに含まれています。

class TheManager {
public:
    virtual SomeManagerClass* someManagerClass() const;
    virtual SomeOtherManager* someOtherManager() const;
    /** More objects... up to 10-20 */
};

現在、TheManager はnew演算子を使用してオブジェクトを作成します。

私の意図は、プラグインを使用して、SomeManagerClass (または他のクラス) の実装を別のものに置き換えることができるようにすることです。実装を置き換えるには、次の 2 つの手順が必要です。

  1. SomeManagerClass を継承する DerivedSomeManagerClass クラスを定義する [プラグイン]
  2. デフォルト (SomeManagerClass) の代わりに新しいクラス (DerivedSomeManagerClass) を作成する [アプリケーション]

ある種のオブジェクト ファクトリが必要だと思いますが、作成するタイプは常に 1 つしかないため (既定の実装またはユーザー実装)、かなり単純なはずです。

今説明したような単純な工場を設計する方法について何か考えはありますか? 将来的にはさらに多くのクラスが存在する可能性があるため、拡張が容易になるはずです。

[*] 複数回発生してもかまいません。

編集: TheManager に含まれるオブジェクトが 3 つ以上あることに注意してください。

4

14 に答える 14

19

SomeManagerClass から継承するクラス (plugin1) を想定すると、型を構築するにはクラス階層が必要です。

class factory
{
public:
    virtual SomeManagerClass* create() = 0;
};

class plugin1_factory : public factory
{
public:
    SomeManagerClass* create() { return new plugin1(); }
};

次に、これらのファクトリを std::map に割り当てることができます。そこで、それらは文字列にバインドされます

std::map<string, factory*>  factory_map;
...
factory_map["plugin1"] = new plugin1_factory();

最後に、TheManager はプラグインの名前を (文字列として) 知っている必要があり、たった 1 行のコードで SomeManagerClass 型のオブジェクトを返すことができます。

SomeManagerClass* obj = factory_map[plugin_name]->create();

編集: プラグインごとに 1 つのプラグイン ファクトリ クラスを使用したくない場合は、前のパターンを次のように変更できます。

template <class plugin_type>
class plugin_factory : public factory
{
public:
   SomeManagerClass* create() { return new plugin_type(); }
};

factory_map["plugin1"] = new plugin_factory<plugin1>();

これははるかに優れたソリューションだと思います。さらに、コストラクタに文字列を渡すと、「plugin_factory」クラスが自身を「factory_map」に追加できます。

于 2009-02-11T14:55:20.007 に答える
3

私はC++ファクトリに関する別のSOの質問に答えました。柔軟な工場に関心があるかどうか、そこで確認してください。ET ++からマクロを使用するための古い方法を説明しようとしていますが、これは私にとって非常に効果的です。

ET ++は、古いMacAppをC++およびX11に移植するプロジェクトでした。その努力の中で、EricGammaなどがデザインパターンについて考え始めました。

于 2009-02-10T22:26:49.587 に答える
2

これは、Abstract Factory パターンとは対照的に、関数テンプレートを使用するとはるかに単純になるようです

class ManagerFactory
{
public:
    template <typename T> static BaseManager * getManager() { return new T();}
};

BaseManager * manager1 = ManagerFactory::template getManager<DerivedManager1>();

文字列を介してそれらを取得したい場合は、文字列から関数ポインターへの標準マップを作成できます。動作する実装は次のとおりです。

#include <map>
#include <string>

class BaseManager
{
public:
    virtual void doSomething() = 0;
};

class DerivedManager1 : public BaseManager
{
public:
    virtual void doSomething() {};
};

class DerivedManager2 : public BaseManager
{
public:
    virtual void doSomething() {};
};

class ManagerFactory
{
public:
    typedef BaseManager * (*GetFunction)();
    typedef std::map<std::wstring, GetFunction> ManagerFunctionMap;
private:
    static ManagerFunctionMap _managers;

public:
    template <typename T> static BaseManager * getManager() { return new T();}
    template <typename T> static void registerManager(const std::wstring& name)
    {
        _managers[name] = ManagerFactory::template getManager<T>;
    }
    static BaseManager * getManagerByName(const std::wstring& name)
    {
        if(_managers.count(name))
        {
            return _managers[name]();
        }
        return NULL;
    }
};
// the static map needs to be initialized outside the class
ManagerFactory::ManagerFunctionMap ManagerFactory::_managers;


int _tmain(int argc, _TCHAR* argv[])
{
    // you can get with the templated function
    BaseManager * manager1 = ManagerFactory::template getManager<DerivedManager1>();
    manager1->doSomething();
    // or by registering with a string
    ManagerFactory::template registerManager<DerivedManager1>(L"Derived1");
    ManagerFactory::template registerManager<DerivedManager2>(L"Derived2");
    // and getting them
    BaseManager * manager2 = ManagerFactory::getManagerByName(L"Derived2");
    manager2->doSomething();
    BaseManager * manager3 = ManagerFactory::getManagerByName(L"Derived1");
    manager3->doSomething();
    return 0;
}

編集: 他の回答を読んで、これは Dave Van den Eynde の FactorySystem ソリューションに非常に似ていることに気付きましたが、テンプレート化されたファクトリ クラスをインスタンス化する代わりに、関数テンプレート ポインターを使用しています。私のソリューションはもう少し軽量だと思います。静的関数のため、インスタンス化される唯一のオブジェクトはマップ自体です。他の機能 (DestroyManager など) を実行するためにファクトリが必要な場合は、彼のソリューションの方が拡張性が高いと思います。

于 2009-02-14T18:57:54.873 に答える
2

これが私が考えた解決策です。これは最善の解決策ではありませんが、より良い解決策を考えるのに役立つかもしれません:

クラスごとに作成者クラスがあります。

class SomeManagerClassCreator {
public:
    virtual SomeManagerClass* create(SomeOtherManager* someOtherManager) { 
        return new SomeManagerClass(someOtherManager); 
    }
};

次に、作成者は 1 つのクラスに集められます。

class SomeManagerClassCreator;
class SomeOtherManagerCreator;

class TheCreator {
public:
    void setSomeManagerClassCreator(SomeManagerClassCreator*);
    SomeManagerClassCreator* someManagerClassCreator() const;

    void setSomeOtherManagerCreator(SomeOtherManagerCreator*);
    SomeOtherManagerCreator* someOtherManagerCreator() const;
private:
    SomeManagerClassCreator* m_someManagerClassCreator;
    SomeOtherManagerCreator* m_someOtherManagerCreator;
};

TheManager は、内部作成用の TheCreator で作成されます。

class TheManager {
public:
    TheManager(TheCreator*);
    /* Rest of code from above */
};

このソリューションの問題は、DRY に違反していることです。各クラス クリエーターに対して、TheCreator でセッター/ゲッターを記述する必要があります。

于 2008-12-02T09:36:06.193 に答える
2

すべての基本マネージャーを作成するための仮想メソッドを持つ「ベース」ファクトリーを作成し、「メタマネージャー」(質問の TheManager ) がコンストラクターパラメーターとしてベースファクトリーへのポインターを取得できるようにします。

「ファクトリー」は CXYZWManager のインスタンスを派生させてカスタマイズできると想定していますが、代わりに CXYZWManager のコンストラクターが「カスタム」ファクトリーで異なる引数を取ることもできます。

「CSomeManager」と「CDerivedFromSomeManager」を出力する長いコード例:

#include <iostream>
//--------------------------------------------------------------------------------
class CSomeManager
  {
  public:
    virtual const char * ShoutOut() { return "CSomeManager";}
  };

//--------------------------------------------------------------------------------
class COtherManager
  {
  };

//--------------------------------------------------------------------------------
class TheManagerFactory
  {
  public:
    // Non-static, non-const to allow polymorphism-abuse
    virtual CSomeManager   *CreateSomeManager() { return new CSomeManager(); }
    virtual COtherManager  *CreateOtherManager() { return new COtherManager(); }
  };

//--------------------------------------------------------------------------------
class CDerivedFromSomeManager : public CSomeManager
  {
  public:
    virtual const char * ShoutOut() { return "CDerivedFromSomeManager";}
  };

//--------------------------------------------------------------------------------
class TheCustomManagerFactory : public TheManagerFactory
  {
  public:
    virtual CDerivedFromSomeManager        *CreateSomeManager() { return new CDerivedFromSomeManager(); }

  };

//--------------------------------------------------------------------------------
class CMetaManager
  {
  public:
    CMetaManager(TheManagerFactory *ip_factory)
      : mp_some_manager(ip_factory->CreateSomeManager()),
        mp_other_manager(ip_factory->CreateOtherManager())
      {}

    CSomeManager  *GetSomeManager()  { return mp_some_manager; }
    COtherManager *GetOtherManager() { return mp_other_manager; }

  private:
    CSomeManager  *mp_some_manager;
    COtherManager *mp_other_manager;
  };

//--------------------------------------------------------------------------------
int _tmain(int argc, _TCHAR* argv[])
  {
  TheManagerFactory standard_factory;
  TheCustomManagerFactory custom_factory;

  CMetaManager meta_manager_1(&standard_factory);
  CMetaManager meta_manager_2(&custom_factory);

  std::cout << meta_manager_1.GetSomeManager()->ShoutOut() << "\n";
  std::cout << meta_manager_2.GetSomeManager()->ShoutOut() << "\n";
  return 0;
  }
于 2008-12-02T10:06:10.250 に答える
1

http://downloads.sourceforge.net/papafactory/PapaFactory20080622.pdf?use_mirror=fastbullのチュートリアルを ご覧ください。

C++ での抽象ファクトリの実装に関する優れたチュートリアルが含まれており、付属のソース コードも非常に堅牢です。

クリス

于 2009-03-12T00:16:35.730 に答える
1

ファクトリ クラスの要点がわからないので、次のようなテンプレートを使用します。

class SomeOtherManager;

class SomeManagerClass {
public:
    SomeManagerClass(SomeOtherManager*);
    virtual void someMethod1();
    virtual void someMethod2();
};


class TheBaseManager {
public:
      // 
};

template <class ManagerClassOne, class ManagerClassOther> 
class SpecialManager : public TheBaseManager {
    public:
        virtual ManagerClassOne* someManagerClass() const;
        virtual ManagerClassOther* someOtherManager() const;
};

TheBaseManager* ourManager = new SpecialManager<SomeManagerClass,SomeOtherManager>;
于 2008-12-02T11:24:48.297 に答える
1

Manager-Class のインスタンスを返す静的メソッドを使用してオブジェクト ファクトリを実装できます。ファクトリでは、デフォルト タイプのマネージャ用のメソッドと、マネージャ クラスのタイプを表す引数を与える任意のタイプのマネージャ用のメソッドを作成できます (列挙型など)。この最後のメソッドは、クラスではなくインターフェイスを返す必要があります。

編集: いくつかのコードを提供しようとしますが、私の C++ の時間はかなり前のものであり、当面は Java といくつかのスクリプトのみを行っていることに注意してください。

class Manager { // aka Interface
    public: virtual void someMethod() = 0;
};

class Manager1 : public Manager {
    void someMethod() { return null; }
};

class Manager2 : public Manager {
    void someMethod() { return null; }
};

enum ManagerTypes {
    Manager1, Manager2
};

class ManagerFactory {
    public static Manager* createManager(ManagerTypes type) {
        Manager* result = null;
        switch (type) {
        case Manager1:
             result = new Manager1();
             break;
        case Manager2:
             result = new Manager2();
             break;
        default:
             // Do whatever error logging you want
             break;
        }
        return result;
     }
 };

これで、次の方法で Factory を呼び出すことができるはずです (コード サンプルを機能させることができた場合)。

Manager* manager = ManagerFactory.createManager(ManagerTypes.Manager1);
于 2008-12-02T09:23:12.100 に答える
0

これは必要以上に重いかもしれませんが、プラグインをサポートするフレームワーククラスを作成しようとしているようです。

私はそれを3つのセクションに分割します。

1)FrameWorkクラスがプラグインを所有します。このクラスは、プラグインによって提供されるインターフェースを公開する責任があります。

2)PlugInクラスは、作業を行うコンポーネントを所有します。このクラスは、エクスポートされたインターフェイスを登録し、インポートされたインターフェイスをコンポーネントにバインドする責任があります。

3)3番目のセクションであるコンポーネントは、インターフェースのサプライヤーとコンシューマーです。

物事を拡張可能にするために、物事を立ち上げて実行することは段階に分割されるかもしれません。

  1. すべてを作成します。
  2. すべてを配線します。
  3. すべてを開始します。

物事を分解する。

  1. すべてを停止します。
  2. すべてを破壊。
クラスIFrameWork{
公衆:
    仮想〜IFrameWork(){}
    virtual void RegisterInterface(const char *、void *)= 0;
    virtual void * GetInterface(const char * name)= 0;
};

クラスIPlugIn{
公衆:
    仮想〜IPlugIn(){}
    virtual void BindInterfaces(IFrameWork * frameWork){};
    virtual void Start(){};
    virtual void Stop(){};
};

struct SamplePlugin:public IPlugIn {
    ILogger*ロガー;

    Component1 component1;
    WebServer webServer;

公衆:
    SamplePlugin(IFrameWork * frameWork)
        :logger((ILogger *)frameWork-> GetInterface( "ILogger"))、//「システム」プラグインがこれを公開すると想定
        component1()、
        webServer(component1)
    {{
        logger-> Log( "MyPlugin Ctor()");

        frameWork-> RegisterInterface( "ICustomerManager"、dynamic_cast(&component1));
        frameWork-> RegisterInterface( "IVendorManager"、dynamic_cast(&component1));
        frameWork-> RegisterInterface( "IAccountingManager"、dynamic_cast(&webServer));
    }

    virtual void BindInterfaces(IFrameWork * frameWork){
        logger-> Log( "MyPlugin BindInterfaces()");

        IProductManager * productManager(static_cast(frameWork-> GetInterface( "IProductManager")));
        IShippingManager * ShippingManager(static_cast(frameWork-> GetInterface( "IShippingManager")));

        component1.BindInterfaces(logger、productManager);
        webServer.BindInterfaces(logger、productManager、shippingManager);
    }

    virtual void Start(){
        logger-> Log( "MyPlugin Start()");

        webServer.Start();
    }

    virtual void Stop(){
        logger-> Log( "MyPlugin Stop()");

        webServer.Stop();
    }
};

クラスFrameWork:public IFrameWork {
    ベクタープラグイン;
    マップインターフェイス;
公衆:
    virtual void RegisterInterface(const char * name、void * itfc){
        interfaces [name] = itfc;
    }
    virtual void * GetInterface(const char * name){
        インターフェイスを返す[名前];
    }

    フレームワーク() {
        //「SystemPlugin」のインターフェースのみが他のプラグインのすべてのメソッドで使用できます
        plugins.push_back(new SystemPlugin(this));

        plugins.push_back(new SamplePlugin(this));
        //ここに他のプラグインを追加します

        for_each(plugIns.begin()、plugIns.end()、bind2nd(mem_fun(&IPlugIn :: BindInterfaces)、this));
        for_each(plugIns.begin()、plugIns.end()、mem_fun(&IPlugIn :: Start));
    }

    〜FrameWork(){
        for_each(plugIns.rbegin()、plugIns.rend()、mem_fun(&IPlugIn :: Stop));
        for_each(plugIns.rbegin()、plugIns.rend()、Delete());
    }
};

于 2009-02-13T14:11:25.383 に答える
0

あなたはTheManagerについて話しませんでした。どのクラスが使用されているかを制御したいようですか?または多分あなたはそれらを一緒に連鎖させようとしていますか?

抽象基本クラスと現在使用されているクラスへのポインタが必要なようです。チェーンしたい場合は、抽象クラスとマネージャークラスの両方で行うことができます。抽象クラスの場合は、チェーン内の次のクラスにメンバーを追加します。マネージャーの場合は、リストで使用する順序でメンバーを並べ替えます。クラスを追加する方法が必要になるため、マネージャーにaddMe()が必要になります。自分が何をしているのかを知っているようですが、あなたが選んだのは正しいはずです。addMe関数を含むリストが私の推奨事項です。アクティブなクラスが1つだけ必要な場合は、TheManagerの関数がそれを決定するのがよいでしょう。

于 2009-02-11T12:21:56.227 に答える
0

Mh 私は 100% 理解できませんし、本や記事からの工場の話にはあまり興味がありません。


すべてのマネージャーが同様のインターフェイスを共有している場合は、基本クラスから派生させ、プログラムでこの基本クラスを使用できます。どのクラスを作成するかを決定する場所に応じて、(前述のように) 作成に識別子を使用するか、どのマネージャーを内部でインスタンス化するかの決定を処理する必要があります。


別の方法は、テンプレートを使用して「ポリシー」を実装することです。You ManagerClass::create() が特定の SomeOtherManagerWhatever インスタンスを返すようにします。これにより、マネージャーを使用するコードでどのマネージャーを決定するかが決まります - Maye これは意図したものではありません。

またはその方法:


template<class MemoryManagment>
class MyAwesomeClass
{
    MemoryManagment m_memoryManager;
};

(またはそのようなもの) この構成を使用すると、MyAwesomeClass のインスタンス化を変更するだけで、他のマネージャーを簡単に使用できます。


また、この目的のためのクラスは少しやり過ぎかもしれません。あなたの場合、ファクトリ関数でうまくいくと思います。まあ、それは個人的な好みの問題です。

于 2008-12-02T09:38:26.767 に答える