设计模式学习(二)工厂模式——工厂方法模式+注册表

· 浏览次数 : 0

小编点评

## 目录工厂方法模式的瑕疵注册表工厂方法模式的瑕疵 **问题:** 在程序运行时,根据外部资源,动态的实例化对象时,如何实现类型注册? **解决方案:** 实现一个类型的注册表,允许动态创建对象。这种方法通过将关键字映射到构造函数指针,使得可以根据字符串名称动态地实例化对象。 **步骤:** 1. **定义一个静态映射** `class_map`,用于存储类和其构造函数指针。 2. **注册类**时,遍历 `class_map`,查找与关键字匹配的类。 3. **获取构造函数指针**,并使用指针创建对象。 4. **清理资源**。 **代码示例:** ```cpp #ifndef Reflection_H #define Reflection_H #include #include template 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 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\n"); return nullptr; } return reinterpret_cast(class_map[vendor_name])(args...); } private: // 获取全局唯一的map static std::map

正文

工厂方法模式的瑕疵

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

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

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

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

与设计模式学习(二)工厂模式——工厂方法模式+注册表相似的内容:

设计模式学习(二)工厂模式——工厂方法模式+注册表

目录工厂方法模式的瑕疵注册表 工厂方法模式的瑕疵 在前一篇笔记中我们介绍了工厂方法模式,示例的类图如下: 考虑一种情况:现在要在程序运行时,根据外部资源,动态的实例化对象。也就是说在编译期我们无法知道要实例化的对象的类型。因此在实例化的过程中,就需要加以判断。 例如,在我的例子中,要根据连接到主机的

【23种设计模式】工厂方法模式(二)

## 前言 在讲述之工厂方法模式前,我们来先了解简单工厂模式,简单工厂模式是最简单的设计模式之一,它虽然不属于GoF的23种设计模式,但是应用也较为频繁,同时它也是学习其他创建型模式的基础。下面我们来先了解下简单工厂模式,然后针对它的缺点来引出工厂方法模式。 ## 简单工厂模式定义 **简单工厂模式

设计模式学习(三):工厂模式

设计模式学习(三):工厂模式 作者:Grey 原文地址: 博客园:设计模式学习(三):工厂模式 CSDN:设计模式学习(三):工厂模式 工厂模式 工厂模式是创建型模式,工厂模式分为:简单工厂,工厂方法和抽象工厂三种类型。 简单工厂 这个模式很简单,比如我们需要制造不同类型的鼠标,我们只需要创建一个鼠

Spring框架中的设计模式(重点学习!!!)

# Spring中的设计模式 Spring框架中用到的设计模式有很多,以下是一些常见的设计模式: 1. 依赖注入(DI)和控制反转(IoC):这是Spring框架最核心的设计模式,它允许开发人员将对象之间的依赖关系从代码中抽离出来,由Spring容器负责管理和注入对象之间的依赖关系。 2. 工厂模式

百度飞桨(PaddlePaddle) - PP-OCRv3 文字检测识别系统 基于 Paddle Serving快速使用(服务化部署 - CentOS 7)

Paddle Serving 是飞桨服务化部署框架,能够帮助开发者轻松实现从移动端、服务器端调用深度学习模型的远程预测服务。 Paddle Serving围绕常见的工业级深度学习模型部署场景进行设计,具备完整的在线服务能力,支持的功能包括多模型管理、模型热加载、基于Baidu-RPC的高并发低延迟响应能力、在线模型A/B实验等,并提供简单易用的Client API。Paddle Serving可以

#AI 1分钟学会,利用AI制作思维导图 (NewBing&X-Mind )

思维导图是一种有效的思考和学习工具,它可以帮助你整理和呈现信息,激发你的创造力和记忆力。但是,传统的思维导图软件往往需要你花费大量的时间和精力来设计和绘制,而且难以修改和分享。有没有一种更简单和智能的方式来制作思维导图呢? 答案是肯定的,那就是利用newbing。newbing是微软推出的一款基于G

Jmeter学习之六_进行https证书处理的工作

# Jmeter 进行https证书处理的工作 ## 背景 ``` 继续学习中,想着能够抓取一下https相关的信息 所以计划些一下处理过程 但是感觉自己这一块比较薄弱. 场景设计这一块应该是专业人去搞, 我这边先只是简单学习了解一下. ``` ## 创建证书 ``` 要使用 keytool 工具创

Lab3 存储过程与触发器

学习SQL语言进行编程的基本方法与技术,能够编写存储过程、触发器解决数据库需要处理的复杂问题。 1、 设计一个存储过程或者自定义函数,练习存储过程的设计方法。 2、 设计触发器,理解触发器的工作原理与设计方法。

[转帖]TiDB 数据库核心原理与架构 [TiDB v6](101)笔记

https://www.jianshu.com/p/01e49a93f671 description: "本课程专为将在工作中使用 TiDB 数据库的开发人员、DBA 和架构师设计。 本门课侧重于 TiDB 数据库的架构和设计原则,这是未来管理、开发、性能调整和故障排除的基础。在学习本课程前,您需要

XXL-JOB定时任务框架(Oracle定制版)

xxl-job是一个轻量级、易扩展的分布式任务调度平台,能够快速开发和简单学习。开放源代码并被多家公司线上产品使用,开箱即用。尽管其确实非常好用,但我在工作中使用的是Oracle数据库,因为xxl-job是针对MySQL设计的,所以使用起来需要进行一些魔改。为了方便后人使用,我已经创建了许多SQL和自增序列,并将其整合到了xxl-job-2.3.0版本中,环境已经在线上正常使用了,所以可以放心使用