写点什么

工厂模式——工厂方法模式 + 注册表

  • 2024-06-04
    福建
  • 本文字数:1968 字

    阅读完需:约 6 分钟

工厂方法模式的瑕疵


前一篇笔记中我们介绍了工厂方法模式,示例的类图如下:



考虑一种情况:现在要在程序运行时,根据外部资源,动态的实例化对象。也就是说在编译期我们无法知道要实例化的对象的类型。因此在实例化的过程中,就需要加以判断。


例如,在我的例子中,要根据连接到主机的相机来实例化相机对象,那么客户端(使用工厂方法创建实例的一方)使用工厂方法模式创建对象的时候,代码可能是这样:

//运行时确定数组大小,且确定后不可改变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;}
复制代码


文章转载自:paw5zx

原文链接:https://www.cnblogs.com/paw5zx/p/18229334

体验地址:http://www.jnpfsoft.com/?from=infoq

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
工厂模式——工厂方法模式+注册表_工厂模式_快乐非自愿限量之名_InfoQ写作社区