轉(zhuǎn)載自 如何實現(xiàn)自動注冊對象Factory
目標(biāo)
了解C++里的Factory模式應(yīng)用場景、實現(xiàn)方法,采用模板實現(xiàn)帶來的便利。
來源
- A C++ Object Factory
- Automatic object factory in C++
- Register an object creator in object factory
- C++11實現(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)思路
- 準(zhǔn)備基類
- Factory單例
- 注冊類構(gòu)造方法
- 獲取類實例
調(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)整
通過實現(xiàn)factory模板能夠?qū)崿F(xiàn)統(tǒng)一的factory。
學(xué)到的內(nèi)容
- Factory模式的實現(xiàn)
- 靜態(tài)變量在自動注冊等處理方面的應(yīng)用
- 在一些場景下如何使用模板替代宏