C++设计模式_08_Factory Method工厂方法模式

news2024/12/26 13:52:21

文章目录

  • 1. “对象创建模式”模式
    • 1.1 典型模式
  • 2. 动机(Motivation)
  • 3. 代码演示Factory Method工厂方法模式
    • 3.1 常规方法
    • 3.2 面向接口的编程
      • 3.2.1 FileSplitter1.cpp
      • 3.2.2 MainForm1.cpp
    • 3.3 Factory Method工厂方法
      • 3.3.1 ISplitterFactory.cpp
      • 3.3.2 MainForm2.cpp
      • 3.3.3 FileSplitter2.cpp
  • 4. 模式定义
  • 5. 结构
  • 6. 要点总结
  • 7. 其他参考

本篇将会介绍Factory Method工厂方法模式,其属于一个新的类别,将其归结到“对象创建模式”,该模式的简介如下:

1. “对象创建模式”模式

通过“对象创建” 模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。

1.1 典型模式

  • Factory Method
  • Abstract Factory
  • Prototype
  • Builder

这四个模式非常接近,解决的是同一个问题,只不过这些问题在演化过程中会有细微的差别,需要四个不同的模式进行应对。

2. 动机(Motivation)

  • 在软件系统中,经常面临着创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化。

  • 如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“具体对象创建工作”的紧耦合?

对C++设计模式_05_Observer 观察者模式中的文件分割器的代码抽象,将观察者模式等跟本篇介绍内容不相关的去除,只突出跟“对象创建模式”相关的代码。

3. 代码演示Factory Method工厂方法模式

3.1 常规方法

常规的方法是,创建一个Splitter类,在类中定义一个核心的split方法,在客户端收集参数之后,创建一个对象,通过对象调用方法完成分割。

这种方法存在什么问题呢?

假如我们在一个变化场景看问题,一般意义上,一个类型,需要看到未来变化的需求,这个时候就需要做抽象类或者接口,这是我们最早讲的设计原则-面向接口的编程

3.2 面向接口的编程

面向接口的编程告诉我们,以一个对象的类型,往往应该声明为一个抽象类或者接口,而不应该声明为具体的类,一旦声明为具体的类,就意味着没有支持未来的变化。

假设上面的代码中只支持二进制文件的分割,但是未来也可能支持文本文件的分割或者图片文件或视频文件的分割等。那么代码就变成如下所示:

3.2.1 FileSplitter1.cpp

class ISplitter{
public:
    virtual void split()=0;
    virtual ~ISplitter(){}
};

class BinarySplitter : public ISplitter{
    
};

class TxtSplitter: public ISplitter{
    
};

class PictureSplitter: public ISplitter{
    
};

class VideoSplitter: public ISplitter{
    
};

3.2.2 MainForm1.cpp

class MainForm : public Form
{
	TextBox* txtFilePath;
	TextBox* txtFileNumber;
	ProgressBar* progressBar;

public:
	void Button1_Click(){


        //面向接口编程的最基础表现形式,变量声明为抽象基类
		ISplitter * splitter=
            new BinarySplitter();//依赖具体类
        
        splitter->split();

	}
};

上述即为面向接口所编写的程序,之前介绍的模式背后都有一个抽象基类,这是面向对象设计模式的基础。

为什么要实现面向接口编程

设计原则-依赖倒置原则:依赖抽象,不依赖实现细节

		ISplitter * splitter=
            new BinarySplitter();//依赖具体类

ISplitter * splitter=是抽象依赖,而new BinarySplitter()是细节依赖,仍然存在细节依赖也是不可以的,简单来说MainForm 在编译时总体还是依赖BinarySplitter存在才能编译通过的,这就是编译时的细节依赖,这就违背了设计原则中的依赖倒置原则。这个问题如何解决呢?

再回过头看“对象创建模式”模式中提到的“通过“对象创建” 模式绕开new,来避免对象创建(new)”,为什么要避免对象创建(new)的原因也就是上面提到new BinarySplitter()带来的细节依赖。“它是接口抽象之后的第一步工作”可以理解为它是面向接口编程必然提出的需求,也就是面向接口编程不能只管ISplitter * splitter=的抽象依赖,而不管new BinarySplitter()的细节依赖,两边都要变为接口,变为依赖抽象。

想一想在C++中创建对象的方法

由于抽象类是不允许创建对象的,利用new和栈上创建对象的方法替换等号右边,实现右边为抽象的方法也是不可行的。

那么是否可以采用一种方法来返回一个对象,并且利用virtual实现运行时依赖,这就引出本篇的重点Factory Method工厂方法。

3.3 Factory Method工厂方法

3.3.1 ISplitterFactory.cpp

//抽象类
class ISplitter{
public:
    virtual void split()=0;
    virtual ~ISplitter(){}
};


//工厂基类
class SplitterFactory{
public:
    virtual ISplitter* CreateSplitter()=0;
    virtual ~SplitterFactory(){}
};

3.3.2 MainForm2.cpp

class MainForm : public Form
{
    SplitterFactory*  factory;//工厂

public:
    
    MainForm(SplitterFactory*  factory){
        this->factory=factory;
    }
    
	void Button1_Click()
    {

       //现在的返回值为ISplitter,但是真正创建的对象交给SplitterFactory未来,其中可以放具体的factory,即FileSplitter2.cpp具体工厂内容 
		ISplitter * splitter=
            factory->CreateSplitter(); //多态new
        
        splitter->split();

	}
};

其中Button1_Click()是可以多次点击的,但SplitterFactory* factory;只需要一个即可(代码如上),MainForm中不需要具体指定具体的工厂,通常通过以下代码外接传递进来的一个具体的factory,例如BinarySplitterFactoryISplitter * splitter=factory->CreateSplitter();创建的就是一个BinarySplitter,这样就可以反复创建BinarySplitter,这个地方的形式我们称为多态new

    MainForm(SplitterFactory*  factory){
        this->factory=factory;
    }

此时可能会有人想,传进来的具体的factory在其他地方也要创建,也会对具体类产生依赖,这是对的,但是在MainForm没有对具体类,至于MainForm以外的是不归MainForm管的。

面向对象设计模式的松耦合设计很多情况下不是消灭变化(依赖具体类),而是将其赶到局部的地方。大家可以将变化比作一只猫,将其关到笼子里,而不是让它在你的代码里面跳来跳去

3.3.3 FileSplitter2.cpp

//具体类
class BinarySplitter : public ISplitter{
    
};

class TxtSplitter: public ISplitter{
    
};

class PictureSplitter: public ISplitter{
    
};

class VideoSplitter: public ISplitter{
    
};

//具体工厂
class BinarySplitterFactory: public SplitterFactory{
public:
    virtual ISplitter* CreateSplitter(){
        return new BinarySplitter();
    }
};

class TxtSplitterFactory: public SplitterFactory{
public:
    virtual ISplitter* CreateSplitter(){
        return new TxtSplitter();
    }
};

class PictureSplitterFactory: public SplitterFactory{
public:
    virtual ISplitter* CreateSplitter(){
        return new PictureSplitter();
    }
};

class VideoSplitterFactory: public SplitterFactory{
public:
    virtual ISplitter* CreateSplitter(){
        return new VideoSplitter();
    }
};

上面代码就是一个完整的Factory Method工厂方法模式。

4. 模式定义

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟(目的:解耦,手段:虚函数)到子类。

​ ——《设计模式》GoF

结合代码来看,“定义一个用于创建对象的接口”指的 就是以下代码,具体即为:virtual ISplitter* CreateSplitter()=0;

//工厂基类
class SplitterFactory{
public:
    virtual ISplitter* CreateSplitter()=0;
    virtual ~SplitterFactory(){}
};

“让子类决定实例化哪一个类”即为以下代码:

//具体工厂
class BinarySplitterFactory: public SplitterFactory{
public:
    virtual ISplitter* CreateSplitter(){
        return new BinarySplitter();
    }
};
......

5. 结构

在这里插入图片描述

上图是《设计模式》GoF中定义的Factory Method工厂方法的设计结构。结合上面的代码看图中最重要的是看其中稳定和变化部分,也就是下图中红框和蓝框框选的部分。

在这里插入图片描述

6. 要点总结

  • Factory Method模式用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱。
  • Factory Method模式通过面向对象的手法(多态),将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
  • Factory Method模式解决“单个对象”的需求变化。缺点在于要求创建方法/参数相同。

第二条中的扩展也就是增加对应的factory即可。延迟而非更改最早的代码中,一旦需求发生变化,就需要在Mainform中更改ISplitter * splitter=new BinarySplitter();//依赖具体类,现在需求变化之后Mainform中ISplitter * splitter=factory->CreateSplitter(); 就不需要改变,只需要增加子类和子类工厂,传给Mainform即可。

7. 其他参考

C++设计模式——工厂方法模式

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

冰河公开了常逛的23大学习网站,赶快收藏

大家好,我是冰河~~ 最近有小伙伴问我:冰河,你有啥好的学习资源或者网站没?分享一下可以吗?这不,我连夜给大家整理了一波,走起~~ 赶快收藏,悄悄努力,然后惊艳所有人&…

睿趣科技:现在开抖音小店到底要多少钱

随着短视频平台的兴起,抖音小店成为了越来越多创业者的选择。那么,现在开抖音小店到底要多少钱呢?这个问题涉及到以下几个方面的费用。 首先,我们需要了解的是,开设抖音小店本身是免费的。你只需要在抖音APP上申请开店&#xff0…

迁移conda环境后,非root用户执行pip命令和jupyter命令报错/bad interpreter: Permission denied

移动conda环境,在移动的环境执行pip和jupyter 报错-bash: /data/home/用户名/anaconda3/envs/llm/bin/pip: /root/anaconda3/envs/llm/bin/python: bad interpreter: Permission denied 报错信息 一、原因 原因是当前的这个data/home/用户名/anaconda3/envs/环境名…

2183440-41-5,138589-19-2,FAM amine, 5-isomer可以与蛋白质或核酸结合

试剂 | 基础知识概述(部分): 【中文名称】FAM-胺,5-异构体 【英文名称】FAM amine, 5-isomer 【CAS】2183440-41-5,138589-19-2 【分子式】C27H27ClN2O6 【分子量】511.0 【Ex】: 490 【Em】: 513 规格标准:10…

文章必备| 叶绿体基因组高级分析内容汇总

叶绿体普遍存在于植物体中,叶绿体基因组是一个典型的双链环状DNA分子,一个植物当中含有多个叶绿体,一个叶绿体中含有12个cpDNA分子。 常见的植物叶绿体基因组大小一般在150-160 kb左右,藻类会略小一些,在80-100 kb左右…

基于RFID技术的烟草供应链数字化管理方案

一、背景介绍 在国家烟草局对烟草行业标准化管理的要求下,工业企业和商业企业纷纷实施烟草编码和扫描,以实现烟草生产和物流过程的跟踪管理。然而,传统的人工管理方法存在工作量大、效率低、错误率高等问题,难以满足现代经济竞争…

Linux下使用openssl为harbor制作证书

openssl是一个功能丰富且自包含的开源安全工具箱。它提供的主要功能有:SSL协议实现(包括SSLv2、SSLv3和TLSv1)、大量软算法(对称/非对称/摘要)、大数运算、非对称算法密钥生成、ASN.1编解码库、证书请求(PKCS10)编解码、数字证书编解码、CRL编解码、OCSP协议、数字证…

做好文件外发管理,助力企业安全高效的进行文件共享

信息化时代,考虑到文件传输的便捷和快速,大多采用电子文件的形式完成。电子文档传播性强,但容易被拷贝传输,因此对于发给客户或合作伙伴的重要资料,都需要做好文件外发管理策略,不然随时可能被以明文的形式…

“火焰杯”软件测试高校就业选拔赛获奖名单揭晓,河南工业大学人工智能与大数据学院两名学子上榜,奖金2万元!

10月14日,由大学生软件测试就业联盟主办的“火焰杯”软件测试高校就业选拔赛颁奖典礼在D204会议室隆重举行。我校人工智能与大数据学院软件工程1803班张志成同学夺得决赛一等奖,奖金20000元;软件工程1904班王博伦同学荣获初赛一等奖和决赛二等…

VSCode搭建ESP32 ESP-IDF开发环境-Windows

陈拓 2023/10/09-2023/10/14 1. 安装Windows系统下的ESP32 ESP-IDF开发环境 见《Windows系统安装ESP32 ESP-IDF开发环境》 Windows系统安装ESP32 ESP-IDF开发环境-CSDN博客Windows系统安装ESP32 ESP-IDF开发环境。https://blog.csdn.net/chentuo2000/article/details/1339225…

编程初学者的“愚蠢”代码

每个编程学习者都曾经是初学者,刚起步的时候难免写下一些令人尴尬的、看似愚蠢的代码。这些代码可能充满了错误、逻辑混乱,但它们是学习过程中的一部分。在本文中,我将分享我自己作为初学者时写的一段Java代码,列出问题、分析原因…

springboot+vue基于Spark的共享单车数据存储系统的设计与实现【内含源码+文档+部署教程】

博主介绍:✌全网粉丝10W,前互联网大厂软件研发、集结硕博英豪成立工作室。专注于计算机相关专业毕业设计项目实战6年之久,选择我们就是选择放心、选择安心毕业✌ 🍅由于篇幅限制,想要获取完整文章或者源码,或者代做&am…

分布式链路追踪系统Skywalking的部署和应用

一,背景 随着业务的扩张, 系统变得越来越复杂, 由前端、app、api,微服务,数据库,缓存,消息队列,关系数据库, 列式数据库等构成了繁杂的分布式网络. 当出现一个调用失败的问题时,要定位异常在哪个服务,需要进入每一个服务里看日志, 这个过程的复杂度和工作量是不可想…

蓝屏代码0xc0000001的解决方法和注意事项

蓝屏代码0xc0000001是Windows系统常见的一个错误代码,它通常意味着系统启动时出现了严重的错误。当用户遇到这个问题时,可能会感到非常困惑和焦虑。本文将为大家介绍一些解决蓝屏代码0xc0000001的方法,并详细说明注意事项。让我们一起来看看&…

deepin操作系统下载

官网 最新版本 – 深度科技社区 下载页面 最新版本 – 深度科技社区 随便选择一个下载 直接下载地址 https://cdimage.deepin.com/releases/20.9/deepin-desktop-community-20.9-amd64.iso

IDEA同步代码到Gitee

参考博客 https://gitee.com/jakhyd/risk-operation.git

通达OA通用版V12的表单js定制开发,良好实践总结-持续更新

通达OA通用版V12的表单js定制开发的良好实践总结-持续更新 良好实践总结在表单中的js区域标准代码2023年10月19日获取地址栏:协议、域名/IP地址端口号获取地址栏的参数,比如run_id、flow_id等向表单中追加自定义css、js文件 良好实践总结 在webroot下的…

电脑出现关于kernelbase.dll文件找不到的情况,有什么办法可以解决?

在使用电脑中,突然提示找不到kernelbase.dll,这时候应该怎么办呢?出现这样的问题,有神办法可以解决。看到有小伙伴在问这个问题,那么今天就带大家了解一下这个文件,同时教大家如何解决kernelbase.dll丢失的…

回溯法介绍-回溯与递归的区别【详细且排版舒服】

一、回溯法 1. 定义 有一类问题,我们不知道它明确的计算法则。而是先进行试探,试探到最终状况,发现不满足问题的要求,则回溯到上一个状态继续试探。这种不断试探和回溯的思想,称为回溯法(Back Track Meth…

华为OD机试 - 机器人走迷宫 - 深度优先搜索dfs(Java 2023 B卷 200分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、深度优先搜索dfs六、Java算法源码七、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题&…