如何實現(xiàn)自動注冊對象Factory

轉(zhuǎn)載自 如何實現(xiàn)自動注冊對象Factory

目標(biāo)

了解C++里的Factory模式應(yīng)用場景、實現(xiàn)方法,采用模板實現(xiàn)帶來的便利。

來源

用途

一系列類繼承自基類,均可以執(zhí)行某個動作,可以根據(jù)類的Key獲取類實例進行動作。

原始版本及存在的問題

class IObject
{
public:
    virtual void run() = 0;
    virtual ~IObject() {};
};

class Object1:public IObject
{
public:
    ~Object1()
    {
        ;
    }
    virtual void run() override
    {
        std::cout<<"Object1\n";
    }
};

class Object2 :public IObject
{
public:
    ~Object2()
    {
        ;
    }

    virtual void run() override
    {
        std::cout << "Object2\n";
    }
};

class ObjectFactory
{
public:
    static std::shared_ptr<IObject> create(const std::string& key)
    {
        if(key == "Object1")
            return std::make_shared<Object1>();
        if(key == "Object2")
            return std::make_shared<Object2>();
        return nullptr;
    }
};

void original_test()
{
    auto pVal1 = ObjectFactory::create("Object1");
    auto pVal2 = ObjectFactory::create("Object2");

    if(pVal1)
        pVal1->run();
    if(pVal2)
        pVal2->run();
}

這是最常規(guī)的實現(xiàn)方法,一旦新增類,則需要調(diào)整factory的創(chuàng)建方法,一旦這些代碼被封裝成庫就沒有擴展的可能;
我們希望沒有非必要的依賴,避免調(diào)整類定義,盡可能簡單就能夠注冊新類。

實現(xiàn)思路

  1. 準(zhǔn)備基類
  2. Factory單例
  3. 注冊類構(gòu)造方法
  4. 獲取類實例

調(diào)整版本

基類

class IObject
{
public:
    virtual void run() = 0;
    virtual ~IObject() {};
};

Factory單例

class ObjectFactory
{
public:
    static ObjectFactory* Instance()
    {
        static ObjectFactory factory;
        return &factory;
    }
};

注冊類構(gòu)造方法

    void registerObjectFunc(const std::string& name, std::function<IObject*()> func)
    {
        funcs_[name] =func;
    }
private:
    std::map<std::string,std::function<IObject*()>> funcs_;

構(gòu)造類實例

std::shared_ptr<IObject> create(const std::string& key)
{
    auto it = funcs_.find(key);
    if (it != funcs_.end())
        return std::shared_ptr<IObject>(it->second());
    return nullptr;
}

使用方法

//類1
class Object1 :public IObject
{
public:
    ~Object1()
    {
        ;
    }
    virtual void run() override
    {
        std::cout << "Object1\n";
    }
};
//類2
class Object2 :public IObject
{
public:
    ~Object2()
    {
        ;
    }

    virtual void run() override
    {
        std::cout << "Object2\n";
    }
};

//使用
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<IObject*()> 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<typename T>
class ObjectRegister
{
public:
    ObjectRegister(const char* key)
    {
        ObjectFactory::Instance()->registerObjectFunc(key,[](){
            return new T();
        });
    }
};

使用方式:

static ObjectRegister<Object1> register1("Object1");
static ObjectRegister<Object2> register2("Object2");

自動注冊的實現(xiàn)

以上的方案都需要在外部定義靜態(tài)變量用來進行構(gòu)造方法注冊,還有方法無需外部定義:

將靜態(tài)變量包裹在類中

約束Key獲取接口

每個類定義均有靜態(tài)接口Key()來獲取Key供注冊使用

template<typename T>
void registerT()
{
    //std::cout << "RegisterT " << T::Key() << "\n";
    funcs_[T::Key()] = []()->IEntry* { return new T(); };
}

定義輔助類

template<typename T>
struct RegisterClass
{
    RegisterClass()
    {
        Factory::Instance().registerT<T>();
    }
};

自動注冊類

I為基類,新類繼承自AutoRegister自動附帶注冊類

template<typename T, typename I>
struct AutoRegister :public I
{
    AutoRegister()
    {
        //?ister_;
    }
public:
    static RegisterClass<T> register_;
};

//模板類靜態(tài)變量聲明->(C++14的變量模板)
template<typename T, typename I>
RegisterClass<T> AutoRegister<T, I>::register_;

使用

class Object :public AutoRegister<Object, IEntry>
{
public:
    void run()
    {
        std::cout << "Object\n";
    }
    //Key配置接口
    static const char* Key()
    {
        //由于VisualStudio2015不支持變量模板,這個可以用來聲明靜態(tài)變量
        Object::register_;
        //在支持變量模板的C++編譯器中這個是不需要的
        return "Object";
    }
};

調(diào)整注冊輔助類

C++11新特性:內(nèi)部類可以通過外部類的實例訪問外部類的私有成員;

struct Factory
{
public:
    template<typename T>
    struct register_helper
    {
        register_helper(const char* key)
        {
            //獲取外部類實例的私有成員變量
            Instance().funcs_[key] = []()->IEntry* { return new T(); };
        }
    };

模板注冊免命名

之前使用宏進行新類注冊會自動合并出靜態(tài)輔助類實例名稱,而使用模板進行新類注冊則需要保證名稱不重復(fù),否則編譯報錯,可以采用以下方式避免該問題:

template<typename T>
struct register_agent
{
    static Factory::register_helper<T> helper;
};

//TObject的注冊輔助類靜態(tài)實例
decltype(register_agent<TObject>::helper) register_agent<TObject>::helper("TObject");
//Object的
decltype(register_agent<Object>::helper) register_agent<Object>::helper("Object");

其它調(diào)整

參見C++11實現(xiàn)一個自動注冊的工廠。

通過實現(xiàn)factory模板能夠?qū)崿F(xiàn)統(tǒng)一的factory。

學(xué)到的內(nèi)容

  • Factory模式的實現(xiàn)
  • 靜態(tài)變量在自動注冊等處理方面的應(yīng)用
  • 在一些場景下如何使用模板替代宏
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容