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

文章目录

  • 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(); //多态newsplitter->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.mzph.cn/news/113349.shtml

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

相关文章

只需五步,在Linux安装chrome及chromedriver(CentOS)

一、安装Chrome 1)先执行命令下载chrome: wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm2)安装chrome yum localinstall google-chrome-stable_current_x86_64.rpm看到下图中的Complete出现则代表安装…

分布式锁之mysql 锁

文章目录 使用数据锁:悲观锁 或者 乐观锁悲观锁乐观锁mysql锁总结 使用数据锁:悲观锁 或者 乐观锁 一个sql:直接更新时判断,在更新中判断库存是否大于0 update table set surplus (surplus - buyQuantity) where id 1 and (surp…

记录阿里云服务器(Centos7.9)部署Thingsboard(3.4.2)遇到的一些问题

记录编译Thingsboard遇到的一些问题 部署了一个thingsboard项目到阿里云服务器上,历时十一天,遇到了很多困难,国内关于Thingsboard的资料确实很少,所以想着写一篇博客记录一下,或许能够给以后编译遇到类似问题的人一些…

Python+playwright 实现Web UI自动化

实现Web UI自动化 技术:Pythonplaywright 目标:自动打开百度浏览器,并搜索“亚运会 金牌榜” 需安装:Playwright (不用安装浏览器驱动) # 使用浏览器,并可视化打开 browser playwright.ch…

tomcat、nginx实现四层转发+七层代理+动静分离实验

实验环境: nginx1——20.0.0.11——客户端 静态页面: nginx2——20.0.0.21——代理服务器1 nginx3——20.0.0.31——代理服务器2 动态页面: tomcat1——20.0.0.12——后端服务器1 tomcat2——20.0.0.22——后端服务器2 实验步骤&…

【CANoe】文件处理_hex文件读取解析

hex文件里面只有00,01,04三种码。那么我们在解析的时候只需要对这三种不同状态的进行不同的解析即可。 hex文件格式的解析,可阅读:HEX文件格式详解 首先创建一个Block的结构体,根据经验我们知道,一个数据…

EthernetIP 转MODBUS RTU协议网关连接FANUC机器人作为EthernetIP通信从站

远创智控YC-EIPM-RTU网关产品是一款高效的数据采集工具,它可以通过各种数据接口与工业领域的仪表、PLC、计量设备等产品连接,实时采集这些设备中的运行数据、状态数据等信息。采集到的数据经过整合和运算等操作后,可以被传输到其他设备或者云…

【C++】stackqueue

适配器是一种设计模式 , 该种模式是将一个类的接口转换成客户希望的另外一个接口 。 虽然 stack 和 queue 中也可以存放元素,但在 STL 中并没有将其划分在容器的行列,而是将其称为 容器适配 器 ,这是因为 stack 和队列只是对其他容…

linux上在docker中使用anaconda创建虚拟环境

conda的一些命令以及创建环境的基本命令可参考:Conda环境搭建以及激活 以及 conda 本地环境常用操作 前言 这里是梳理linux上在docker中使用conda,以配置MLD-TResNet-L-AAM模型为例。论文笔记参考:多标签分类论文笔记 | Combining Metric Lea…

分类选择,最多五级

效果图&#xff0c;这种竖向的分类选择&#xff0c;每一列可以用不同的背景颜色 组件代码 <template><view class"toolTypeBox" :style"max-height:${maxHeight}"><block v-for"(item,index) in datalist"><block v-if&…

企业c#语言源代码防泄密解决方案

在当今数字化时代&#xff0c;企业的核心业务往往依赖于软件应用程序。为了保护企业的知识产权和敏感信息&#xff0c;源代码的保密至关重要。对于制造类企业尤其是智能制造业来讲&#xff0c;最近几年是高速发展的时期&#xff0c;很多公司在做工厂流水线设备时&#xff0c;就…

Python学习笔记——类、魔术方法

食用说明&#xff1a;本笔记适用于有一定编程基础的伙伴们。希望有助于各位&#xff01; 类 类的运用很常见&#xff1a;在大部分情况下&#xff0c;对一些特有的对象&#xff0c;可以使用特定的类来指向它&#xff1a; class Person:name unknownage -1sex 0partner No…

微信小程序三种授权登录以及授权登录流程讲解

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《微信小程序开发实战》。&#x1f3af;&#x1f3a…

小县城蔬菜配送小程序制作全攻略

随着互联网的普及和人们对生活品质要求的提高&#xff0c;越来越多的小县城开始开发蔬菜配送小程序&#xff0c;以满足当地居民对新鲜蔬菜的需求。制作一个小县城蔬菜配送小程序&#xff0c;需要经过以下步骤&#xff1a; 步骤一&#xff1a;登录乔拓云平台 首先&#xff0c;打…

揭开 Amazon Bedrock 的神秘面纱 | 基础篇

在 2023 年 4 月&#xff0c;亚马逊云科技曾宣布将 Amazon Bedrock 纳入使用生成式人工智能进行构建的新工具集。Amazon Bedrock 是一项完全托管的服务&#xff0c;提供各种来自领先 AI 公司&#xff08;包括 AI21 Labs、Anthropic、Cohere、Stability AI 和 Amazon 等&#xf…

Android音视频开发之基础知识

一、视频文件 1、视频格式 常见格式&#xff1a;mp4、mkv、flv 封装的数据&#xff1a;音频码流、视频码流 常用工具&#xff1a; [FFmpeg下载]:https://ffmpeg.org/download.html 下载、安装并配置环境变量 ffmpeg.exe 视频编解码 ffplay.exe 播放器库 ffprobe.exe 音视频分…

git(部分)

1、git三个区域&#xff1a;工作区&#xff0c;暂存区&#xff0c;版本库 2、git文件状态&#xff1a;未跟踪&#xff0c;已跟踪&#xff08;新添加&#xff0c;未修改&#xff0c;已修改&#xff09; 如何查看暂存区和工作区文件状态&#xff1a;git status -s 3、查看版本记…

mysql MVC jsp实现表分页

mysql是轻量级数据库 在三层架构中实现简单的分页 在数据库sql编程中需要编写sql语句 SELECT * FROM sys.student limit 5,5; limit x,y x是开始节点&#xff0c;y是开始节点后的需要显示的长度。 在jdbc编程中需要给出x和y 一般是页数*页码&#xff0c;显示的长度。 代…

【(数据结构)—— 基于单链表实现通讯录】

&#xff08;数据结构&#xff09;—— 基于单链表实现通讯录 一.通讯录的功能介绍1.基于单链表实现通讯录(1). 知识要求(2). 功能要求 二.通讯录的代码实现1.通讯录的底层结构(单链表)(1).思路展示(2).底层代码实现(单链表)1.单链表头文件 —— &#xff08;函数的定义&#x…

【分类讨论】CF1747D

Problem - D - Codeforces 题意 思路 一看这个做法一定就是分类讨论 先判无解 显然&#xff0c;如果区间异或和不是0一定无解 如果区间内全是0&#xff0c;答案一定是0 之后怎么讨论 注意到需要讨论区间长度 如果长度是奇数&#xff0c;那么直接操作即可&#xff0c;答…