目錄
- 工廠方法模式的瑕疵
- 登錄檔
工廠方法模式的瑕疵
在前一篇筆記中我們介紹了工廠方法模式,示例的類圖如下:
考慮一種情況:現在要在程式執行時,根據外部資源,動態的例項化物件。也就是說在編譯期我們無法知道要例項化的物件的型別。因此在例項化的過程中,就需要加以判斷。
例如,在我的例子中,要根據連線到主機的相機來例項化相機物件,那麼客戶端(使用工廠方法建立例項的一方)使用工廠方法模式建立物件的時候,程式碼可能是這樣:
//執行時確定陣列大小,且確定後不可改變
auto camera_devices_ = std::make_unique<std::shared_ptr<CameraDevice>[]>(onlined_camera_num_);
for(int i = 0; i < onlined_camera_num_; ++i)
{
std::shared_ptr<CameraDeviceFactory> factory;
if("Sick" == camera_name[i]) //camera_name[i]中元素是提前獲取的與連線的相機對應的供應商名稱
factory = std::make_shared<SickCameraFactory>();
else if("Basler" == camera_name[i])
factory = std::make_shared<BaslerCameraFactory>();
else if("Huaray" == camera_name[i])
factory = std::make_shared<HuarayCameraFactory>();
camera_devices_[i] = factory->CreateCamera();
}
雖然工廠方法模式遵循了開閉原則,即當有新型別的時候,無需修改現有的程式碼,只需新加產品類和對應工廠類即可。但是對於客戶端來說,當需要例項化的型別數量增加時,就需要新增else if去適配,這使得客戶端程式碼變得冗長且難以維護。
登錄檔
為了解決上面問題,我們可以實現一個型別的登錄檔,允許動態建立物件。這種方法透過將關鍵字對映到建構函式指標,使得可以根據字串名稱動態地例項化物件。
#ifndef Reflection_H
#define Reflection_H
#include <map>
#include <string>
template <typename T, typename... ArgType>
void* CreateInstance(ArgType... args)
{
return new T(args...);
}
//需要反射的類使用該宏註冊
#ifndef ReflectRegister
#define ReflectRegister(identifier, class_name, ...) \
static bool __type##class_name = Object::Register(identifier, (void*)CreateInstance<class_name, ##__VA_ARGS__>);
#endif
class Object
{
public:
template <typename BaseClass, typename... ArgType>
static BaseClass *CreateObject(const std::string &vendor_name, ArgType... args)
{
using CreateFactory = BaseClass *(*)(ArgType...);
auto& class_map = GetStaticFuncMap();
auto iter = class_map.find(vendor_name);
if (iter == class_map.end())
{
CRRC_ERROR("class_name not found in map");
return nullptr;
}
else
{
CRRC_DEBUG("class_name found in map");
return reinterpret_cast<CreateFactory>(class_map[vendor_name])(args...);
}
}
//向map中註冊關鍵字和類的建構函式
static bool Register(const std::string &vendor_name, void *ctor_ptr)
{
CRRC_DEBUG("Register class_name:"<<vendor_name);
GetStaticFuncMap()[vendor_name] = ctor_ptr;
return true;
}
private:
//獲取全域性唯一的map
//map記錄了關鍵字和類的建構函式的對映關係
static std::map<std::string, void*>& GetStaticFuncMap()
{
static std::map<std::string, void*> class_map_;
return class_map_;
}
};
#endif //Reflection_H
在具體相機工廠中,我們可以使用ReflectRegister
註冊此類(以Basler相機為例,其餘類似):
class BaslerCameraDeviceFactory : public CameraDeviceFactory
{
public:
std::shared_ptr<CameraDevice> CreateCameraDevice() override
{
return std::make_shared<BaslerCameraDevice>();
}
};
ReflectRegister("Basler", BaslerCameraDeviceFactory);
好了,現在回頭再看客戶端使用工廠方法模式建立物件的程式碼,就可以簡化為:
//執行時確定陣列大小,且確定後不可改變
auto camera_devices_ = std::make_unique<std::shared_ptr<CameraDevice>[]>(onlined_camera_num_);
for(int i = 0; i < onlined_camera_num_; ++i)
{
auto p_factory = Object::CreateObject<CameraDeviceFactory>(camera_name[i]);//camera_name[i]中元素是提前獲取的與連線的相機對應的供應商名稱
if (!p_factory)
continue;
else
camera_devices_[i] = p_factory->CreateCameraDevice();
delete p_factory;
}