自動登録オブジェクトFactoryの実装方法

6546 ワード

ターゲット
C++の中のFactoryモードの応用シーン、実現方法を理解し、テンプレートを採用して実現することがもたらす便利さ.
ソース
  • A C++ Object Factory
  • Automatic object factory in C++
  • Register an object creator in object factory
  • C++11自動登録工場
  • を実現
    用途
    一連のクラスはベースクラスから継承され、いずれもある動作を実行することができ、クラスのKeyに基づいてクラスインスタンスを取得して動作することができる.
    元のバージョンと問題点
    class IObject
    {
    public:
        virtual void run() = 0;
        virtual ~IObject() {};
    };
     
    class Object1:public IObject
    {
    public:
        ~Object1()
        {
            ;
        }
        virtual void run() override
        {
            std::cout< create(const std::string& key)
        {
            if(key == "Object1")
                return std::make_shared();
            if(key == "Object2")
                return std::make_shared();
            return nullptr;
        }
    };
     
    void original_test()
    {
        auto pVal1 = ObjectFactory::create("Object1");
        auto pVal2 = ObjectFactory::create("Object2");
     
        if(pVal1)
            pVal1->run();
        if(pVal2)
            pVal2->run();
    }
    

    これは最も一般的な実装方法であり、クラスが追加されるとfactoryの作成方法を調整する必要があり、これらのコードがライブラリにカプセル化されると拡張する可能性はありません.クラス定義の調整を避け、できるだけ簡単に新しいクラスを登録できるように、不要な依存がないことを望んでいます.
    実現構想.
  • ベースクラス
  • を準備する
  • Factory単例
  • 登録クラス構築方法
  • 取得クラスインスタンス
  • バージョンの調整
    ベースクラス
    class IObject
    {
    public:
        virtual void run() = 0;
        virtual ~IObject() {};
    };
    

    Factoryの一例
    class ObjectFactory
    {
    public:
        static ObjectFactory* Instance()
        {
            static ObjectFactory factory;
            return &factory;
        }
    };
    

    クラス構築メソッドの登録
        void registerObjectFunc(const std::string& name, std::function func)
        {
            funcs_[name] =func;
        }
    private:
        std::map<:string>> funcs_;
    

    クラスインスタンスの構築
    std::shared_ptr create(const std::string& key)
    {
        auto it = funcs_.find(key);
        if (it != funcs_.end())
            return std::shared_ptr(it->second());
        return nullptr;
    }
    

    使用方法
    // 1
    class Object1 :public IObject
    {
    public:
        ~Object1()
        {
            ;
        }
        virtual void run() override
        {
            std::cout << "Object1
    "; } }; // 2 class Object2 :public IObject { public: ~Object2() { ; } virtual void run() override { std::cout << "Object2
    "; } }; // auto pVal1= ObjectFactory::Instance()->create("Object1"); auto pVal2 = ObjectFactory::Instance()->create("Object2"); if (pVal1) pVal1->run(); if (pVal2) pVal2->run();

    新規クラス登録
    static ObjectRegisterHelper Object1_ObjectRegisterHelper("Object1", []()->IObject* { return new Object1();  });
    static ObjectRegisterHelper Object2_ObjectRegisterHelper("Object2", []()->IObject* { return new Object2();  });
    

    マクロによる新規クラス登録
    補助登録:
    class ObjectRegisterHelper
    {
    public:
        ObjectRegisterHelper(const char* key, std::function func)
        {
            ObjectFactory::Instance()->registerObjectFunc(key,func);
        }
    };
     
    #define REGISTER_OBJECT(className,key) \
    static ObjectRegisterHelper className##ObjectRegisterHelper(key,[]()->className*{ return new className();})
    

    使用方法:
    REGISTER_OBJECT(Object1,"Object1");
    REGISTER_OBJECT(Object2, "Object2");
    

    テンプレートを使用した新規クラス登録
    補助登録:
    template
    class ObjectRegister
    {
    public:
        ObjectRegister(const char* key)
        {
            ObjectFactory::Instance()->registerObjectFunc(key,[](){
                return new T();
            });
        }
    };
    

    使用方法:
    static ObjectRegister register1("Object1");
    static ObjectRegister register2("Object2");
    

    自動登録の実装
    以上のシナリオでは、構築メソッドの登録に静的変数を外部で定義する必要があります.また、外部定義を必要としない方法もあります.
    静的変数をクラスにラップ
    制約キー取得インタフェース
    各クラス定義は、登録のためにKeyを取得するための静的インタフェースKey()を有する.
    template
    void registerT()
    {
        //std::cout << "RegisterT " << T::Key() << "
    "; funcs_[T::Key()] = []()->IEntry* { return new T(); }; }

    補助クラスの定義
    template
    struct RegisterClass
    {
        RegisterClass()
        {
            Factory::Instance().registerT();
        }
    };
    

    クラスの自動登録
    Iはベースクラスであり、新しいクラスはAutoRegister自動付帯登録クラスから継承される
    template
    struct AutoRegister :public I
    {
        AutoRegister()
        {
            //®ister_;
        }
    public:
        static RegisterClass register_;
    };
    
    //         ->(C++14     )
    template
    RegisterClass AutoRegister::register_;
    

    使用
    class Object :public AutoRegister
    {
    public:
        void run()
        {
            std::cout << "Object
    "; } //Key static const char* Key() { // VisualStudio2015 , Object::register_; // C++ return "Object"; } };

    登録補助クラスの調整
    C++11の新しい特性:内部クラスは外部クラスのインスタンスを通じて外部クラスのプライベートメンバーにアクセスすることができる;
    struct Factory
    {
    public:
        template
        struct register_helper
        {
            register_helper(const char* key)
            {
                //              
                Instance().funcs_[key] = []()->IEntry* { return new T(); };
            }
        };
    

    テンプレート登録の名前なし
    マクロを使用して新しいクラス登録を行うと、静的アシストクラスインスタンス名が自動的にマージされますが、テンプレートを使用して新しいクラス登録を行うには、名前が重複しないことを保証する必要があります.そうしないと、コンパイルエラーが発生し、この問題を回避するには、次の方法を使用します.
    template
    struct register_agent
    {
        static Factory::register_helper helper;
    };
    
    //TObject          
    decltype(register_agent::helper) register_agent::helper("TObject");
    //Object 
    decltype(register_agent::helper) register_agent::helper("Object");
    

    その他の調整
    C++11を参照して、自動的に登録されたファクトリを実装します.
    factoryテンプレートを実現することで、統一されたfactoryを実現することができる.
    学んだこと
  • Factoryモードの実装
  • 静的変数の自動登録等の処理における応用
  • マクロ
  • の代わりにテンプレートを使用する方法