设计模式学习(二)工厂模式——抽象工厂模式

设计模式学习(二)工厂模式——抽象工厂模式

  • 背景
  • 抽象工厂模式
  • 优点与缺点
  • 参考文章

背景

现在我需要开发一个相机操作模块,它可能在Windows下运行,也可能在Linux下运行。由于在厂家提供的SDK中,Windows下的SDK和Linux下的SDK是有区别的,因此对于一个品牌的相机,我们要创建两个类去封装这两个不同平台下的API。

我们先使用工厂方法模式去设计(以Basler相机为例),类图如下:
在这里插入图片描述

对应的代码(就不用智能指针了,要不然类图不好画):

class BaslerCamera
{
public:
    virtual ~BaslerCamera() = default;
    virtual bool OpenCamera() = 0;
};

class LinuxBaslerCamera : public BaslerCamera
{
public:
    ~LinuxBaslerCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};

class WindowsBaslerCamera : public BaslerCamera
{
public:
    ~WindowsBaslerCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};

class CameraFactory
{
public:
    virtual ~CameraFactory() = default;
    virtual BaslerCamera* CreateBaslerCamera() = 0;
};

class LinuxCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new LinuxBaslerCamera();
    }
};

class WindowsCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new WindowsBaslerCamera();
    }
};

//客户端
int main()
{
	//如果更换平台,客户端代码只需要修改这一处
    CameraFactory* cameraFactory = new LinuxCameraFactory();
    
    BaslerCamera* camera = cameraFactory->CreateBaslerCamera();
    
    camera->OpenCamera();
    
    return 0;
}

现在若新增了一个品牌的相机:Sick,那么按照工厂方法模式的设计思路,就会为其创建出对应的抽象工厂类和具体工厂类(具体代码略)。

但是进一步分析可以发现,对于这个模块,它要么在Windows下运行,要么在Linux下运行。即对于抽象产品BaslerCameraSickCamera,要么实例化LinuxBaslerCameraLinuxSickCamera,要么实例化WindowsBaslerCameraWindowsSickCamera

可以说不同的相机被划分在Linux相机和Window相机这两个产品族下,因此我们不需要为每一个品牌的相机都去实现一组对应的工厂类,而是只使用两个工厂WindowsCameraFactoryLinuxCameraFactory去管理各自对应平台下的相机的创建过程。

那么工厂类的代码就会变成这样:

class CameraFactory
{
public:
    virtual ~CameraFactory() = default;
    virtual BaslerCamera* CreateBaslerCamera() = 0;
    virtual SickCamera* CreateSickCamera() = 0;
};

class LinuxCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new LinuxBaslerCamera();
    }

    SickCamera* CreateSickCamera() override
    {
        return new LinuxSickCamera();
    }
};

class WindowsCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new WindowsBaslerCamera();
    }

    SickCamera* CreateSickCamera() override
    {
        return new WindowsSickCamera();
    }
};

这就引出了抽象工厂模式

抽象工厂模式

抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类

在这里插入图片描述
AbstractProductAAbstractProductB是两个抽象产品,之所以为抽象,是因为他们可能有多种不同的实现,就刚才的例子来说,抽象产品就是BaslerCameraSickCameraProductA1ProductA2ProductB1ProductB2就是对两个抽象产品的具体分类的实现,对应例子中的LinuxBaslerCameraWindowsBaslerCameraLinuxSickCameraWindowsSickCamera

AbstractFactory是一个抽象工厂基类,对应例子中的CameraFactory,它里面应该包含产品族中每个产品创建的抽象方法。ConcreteFactory1ConcreteFactory2是具体工厂,对应例子中的LinuxCameraFactoryWindowsCameraFactory

对于客户端,通常是在代码中创建一个具体工厂的实例(这个实例就对应着一个产品族),使用这个工厂实例再创建产品族内具体的产品对象。

客户端代码如下:

int main()
{
    /*
    若在windows平台,只需将本句改为:
    CameraFactory* cameraFactory = new WindowsCameraFactory();
    */
    CameraFactory* camera_factory = new LinuxCameraFactory();
    
    BaslerCamera* basler_camera = camera_factory->CreateBaslerCamera();
    basler_camera->OpenCamera();

    SickCamera* sick_camera = camera_factory->CreateSickCamera();
    sick_camera->OpenCamera();

    return 0;
}

完整代码如下:

class BaslerCamera
{
public:
    virtual ~BaslerCamera() = default;
    virtual bool OpenCamera() = 0;
};

class LinuxBaslerCamera : public BaslerCamera
{
public:
    ~LinuxBaslerCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};

class WindowsBaslerCamera : public BaslerCamera
{
public:
    ~WindowsBaslerCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};

class SickCamera
{
public:
    virtual ~SickCamera() = default;
    virtual bool OpenCamera() = 0;
};

class LinuxSickCamera : public SickCamera
{
public:
    ~LinuxSickCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};

class WindowsSickCamera : public SickCamera
{
public:
    ~WindowsSickCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};


class CameraFactory
{
public:
    virtual ~CameraFactory() = default;
    virtual BaslerCamera* CreateBaslerCamera() = 0;
    virtual SickCamera* CreateSickCamera() = 0;
};

class LinuxCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new LinuxBaslerCamera();
    }

    SickCamera* CreateSickCamera() override
    {
        return new LinuxSickCamera();
    }
};

class WindowsCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new WindowsBaslerCamera();
    }

    SickCamera* CreateSickCamera() override
    {
        return new WindowsSickCamera();
    }
};

int main()
{
    //若在windows平台,只需将本句改为CameraFactory* cameraFactory = new WindowsCameraFactory();
    CameraFactory* camera_factory = new LinuxCameraFactory();
    
    BaslerCamera* basler_camera = camera_factory->CreateBaslerCamera();
    basler_camera->OpenCamera();

    SickCamera* sick_camera = camera_factory->CreateSickCamera();
    sick_camera->OpenCamera();

    return 0;
}

优点与缺点

优点:

  • 易于交换产品族:工厂的实例化过程在一个客户端只需要出现一次,修改方便

缺点:

  • 提供方违反开闭原则:如果现在在每个产品族内新增一个品牌相机(如Huaray),那么除了要增加HuarayCameraWindowsHuarayCameraLinuxHuarayCamera三个产品类之外(这是必要的),还要修改CameraFactoryLinuxCameraFactoryWindowsCameraFactory这三个工厂类,违反了开闭原则。
  • 客户端违法开闭原则:客户端在开始的时候都要CameraFactory* camera_factory = new LinuxCameraFactory();,若是要更换为Windows平台,则还需手动修改实例化的类型,违反了开闭原则。而且如果客户端不止一个,则每一个客户端都需要手动修改,效率低。

对于抽象工厂模式的改进方法,将在下一篇文章中讨论。

参考文章

1.《大话设计模式》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/800025.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

K8S 中的 CRI、OCI、CRI shim、containerd

哈喽大家好,我是咸鱼。 好久没发文了,最近这段时间都在学 K8S。不知道大家是不是和咸鱼一样,刚开始学 K8S、Docker 的时候,往往被 CRI、OCI、CRI shim、containerd 这些名词搞得晕乎乎的,不清楚它们到底是干什么用的。…

踩坑日记 | 记一次流程图问题排查

踩坑日记:记一次流程图问题排查 标签: activiti | 流程 引言 今天排查了一个流程图问题,耗时2个小时终于解决,记录下来 现象 流程审批驳回报错:Unknown property used in expression: ${xxxx} 使用的是 activiti …

AI视频教程下载-ChatGPT速成课程:工作中的ChatGPT入门

使用ChatGPT提升你的生产力:利用OpenAI的革命性ChatGPT模型。 你准备好深入人工智能交流的世界,彻底改变你的职业生涯了吗?本课程适合技术背景和非技术背景的人士,它以独特、有趣且专业的方式,教授如何使用OpenAI的Ch…

【Linux取经之路】Linux常见指令

目录 基本指令 常见指令 1)ls —— 对于目录,列出该目录下的所有子目录和文件;对于文件,将列出文件名及其他信息 2)pwd —— 显示当前所在的目录 ​编辑 3)cd —— 切换到指定路径下 4)t…

服务客户,保证质量:腾讯云产品的质量实践

分享主题是“服务客户,保证质量”。自从20年开始,我们把质量提升到了一个前所未有的高度。为什么会如此重视质量呢?在竞争激烈和复杂的市场环境中,产品质量对于企业的重要性不言而喻。一旦出现了质量事故,对客户和企业…

实战案例:用百度千帆大模型API开发智能五子棋

前随着人工智能技术的迅猛发展,各种智能应用层出不穷。五子棋作为一款经典的棋类游戏,拥有广泛的爱好者。将人工智能技术与五子棋结合,不仅能提升游戏的趣味性和挑战性,还能展现AI在复杂决策问题上的强大能力。在本篇文章中&#…

CV12_ONNX转RKNN模型(谛听盒子)

暂时简单整理一下: 1.在边缘设备上配置相关环境。 2.配置完成后,获取模型中间的输入输出结果,保存为npy格式。 3.将onnx格式的模型,以及中间输入输出文件传送到边缘设备上。 4.编写一个python文件用于转换模型格式&#xff0c…

LLM之Prompt(四)| OpenAI、微软发布Prompt技术报告

摘要 生成式人工智能 (GenAI) 系统正越来越多地部署在各行各业和研究机构。开发人员和用户通过使用提示或提示工程与这些系统进行交互。虽然提示是一个广泛提及且被研究的概念,但由于该领域的新生,存在相互矛盾的术语和对构成提示…

Spring MVC 全注解开发

1. Spring MVC 全注解开发 文章目录 1. Spring MVC 全注解开发2. web.xml 文件 的替代2.1 Servlet3.0新特性2.2 编写 WebAppInitializer 3. Spring MVC的配置3.1 Spring MVC的配置:开启注解驱动3.2 Spring MVC的配置:视图解析器3.3 Spring MVC的配置&…

IP-Guard日志数据上传至 SYSLOG 服务器操作指南

一、功能简介 服务器支持把日志数据上传到 SYSLOG 服务器。 二、功能配置 2.1 数据目录移交设置 在服务器安装目录下 OServer3.ini 文件中,添加工具启动配置,配置五分钟内生效。 Path:设置移交目录路径,IPG 服务器会把收集完成的…

【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【22】【RabbitMQ】

持续学习&持续更新中… 守破离 【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【22】【RabbitMQ】 Message Queue 消息队列异步处理应用解耦流量控制 消息中间件概念RabbitMQ概念MessagePublisherExchangeQueueBindingConnectionChannelConsumerVirtual HostBroker图…

Spring Boot整合Minio实现文件上传和读取

文章目录 一、简介1.分布式文件系统应用场景2.Minio介绍3.Minio优点 二、docker部署(windows系统)1.创建目录2.拉取镜像3.创建容器并运行4.访问控制台5.初始化配置 三、Spring Boot整合Minio1.创建demo项目2.引入依赖3.配置4.编写配置类5.MinIO工具类6.文…

【C++PythonJava】字符处理详细解读_字符_ASCLL码_字母数字转换_算法竞赛_开发语言

文章目录 Beginning1)ASCLL 码2)大小比较2)判断数字字符3)字符、数字间的相互转换End Beginning 在 C 中,字符和整数有着密不可分的关系。原因就是在计算机中,字符是以一种较 ASCLL 码的整数存储的。自然&…

中科微电子ATGM336H GPS定位模块STM32应用

文章目录 前言1. 中科微电子ATGM336H的使用1.1 ATGM336H引脚说明1.2 数据帧介绍1.3 经纬度介绍1.4 ATGM336H的启动方式 2 数据处理前置C语言知识2.1 strstr函数2.2 memset函数2.3 memcpy函数2.4strtod函数 3. 开始移植3.1 usart初始化程序3.2 串口中断接收函数3.4 数据帧的解析…

—张pdf怎么分割成多页,怎么把一个pdf分割

在数字化时代,pdf文件已经成为我们工作和生活中不可或缺的一部分。然而,有时候我们可能会遇到需要将一张pdf文件分割成多页的情况。无论是为了便于分享,还是为了满足特定的文档格式要求,这个任务都可能变得相当棘手。但别担心&…

17098 广告牌最佳安放问题

这个问题可以通过动态规划来解决。我们可以定义一个数组d&#xff0c;其中d[i]表示到第i个广告牌地点时可以选择放置广告牌的最大效益值。然后我们可以通过遍历所有可能的j&#xff08;1 < j < i && x[i] - x[j] > 5&#xff09;&#xff0c;然后更新d[i]为ma…

【云原生】ptcpdump捕获任何进程、容器或 Pod 的网络流量的抓包神器——筑梦之路

ptcpdump 是一个使用 eBPF 技术开发的、类 tcpdump 的网络抓包工具。它除了兼容 tcpdump 的常用命令行参数以及包过滤语法外&#xff0c; 还额外提供了如下核心特性&#xff1a; 在输出中记录和显示发送网络流量的进程、容器、Pod 信息。 支持对指定进程、容器以及 Pod 进行抓…

LED显示屏中什么情况下用网线?什么情况下用光纤?

在这个色彩斑斓的数字时代&#xff0c;LED显示屏如同城市的眼睛&#xff0c;闪烁着各种信息与艺术的光芒。而要让这些“眼睛”明亮有神&#xff0c;背后离不开两条重要的“信息高速公路”——网线和光纤。它们就像是LED显示屏的血管&#xff0c;负责输送数据这一“血液”。那么…

实验三:图像的平滑滤波

目录 一、实验目的 二、实验原理 1. 空域平滑滤波 2. 椒盐噪声的处理 三、实验内容 四、源程序和结果 (1) 主程序&#xff08;matlab&#xff09; (2) 函数GrayscaleFilter (3) 函数MeanKernel (4) 函数MedFilter 五、结果分析 1. 空域平滑滤波 2. 椒盐噪声的处理…

Python PDF文件的加密和水印处理使用详解

概要 在日常工作中,处理PDF文件是非常常见的需求。为了保护PDF文件的内容,我们可能需要对其进行加密。同时,为了防止文件被未经授权的复制和使用,添加水印也是一种有效的方法。本文将详细介绍如何使用Python对PDF文件进行加密和添加水印的操作,包含详细的示例代码,帮助全…