设计模式-对象创建

对象创建

  • 前言
  • 1. Factory Method
    • 1.1 模式介绍
    • 1.2 模式代码
      • 1.2.1 问题代码
      • 1.2.2 重构代码
    • 1.3 模式类图
    • 1.4 要点总结
  • 2. Abstract Factory
    • 2.1 模式介绍
    • 2.2 模式代码
      • 2.2.1 问题代码
      • 2.2.2 重构代码
    • 2.3 模式类图
    • 2.4 要点总结
  • 3. Prototype
    • 3.1 模式介绍
    • 3.2 模式代码
    • 3.3 模式类图
    • 3.4 要点总结
  • 4. Builder
    • 4.1 模式介绍
    • 4.2 模式代码
    • 4.3 模式类图
    • 4.4 要点总结

前言

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

典型模式:

  • Factory Method 工厂方法
  • Abstract Factory 抽象工厂
  • Prototype 原型模式
  • Builder 构建器模式

1. Factory Method

1.1 模式介绍

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

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

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

——《设计模式》GoF

1.2 模式代码

1.2.1 问题代码

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{};class MainForm : public Form
{TextBox* txtFilePath;TextBox* txtFileNumber;ProgressBar* progressBar;public:void Button1_Click(){ISplitter * splitter=new BinarySplitter();//依赖具体类splitter->split();}
};

你有一个按钮,点击按钮以后可以对二进制文件、文本文件、图片文件、视频文件进行分割,如果按照上述代码进行编写,其依赖具体类,如果你想增加其他文件格式方法时,得修改button里的方法,这会使得封装性被破坏

1.2.2 重构代码

//抽象类
class ISplitter{public:virtual void split()=0;virtual ~ISplitter(){}};//工厂基类
class SplitterFactory{
public:virtual ISplitter* CreateSplitter()=0;virtual ~SplitterFactory(){}
};//具体类
class BinarySplitter : public ISplitter{virtual void split(){}};class TxtSplitter: public ISplitter{virtual void split(){}
};class PictureSplitter: public ISplitter{virtual void split(){}
};class VideoSplitter: public ISplitter{virtual void split(){}
};//具体工厂
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();}
};class MainForm : public Form
{SplitterFactory*  factory;//工厂public:MainForm(SplitterFactory*  factory){this->factory=factory;}void Button1_Click(){ISplitter * splitter=factory->CreateSplitter(); //多态newsplitter->split();}
};

解决方法:设计一个工厂基类,声明一个创建对象的接口,对象基础工厂基类,实现创建对象的方法,即可实现运行时绑定

1.3 模式类图

在这里插入图片描述

1.4 要点总结

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

2. Abstract Factory

2.1 模式介绍

动机:在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。

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

提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。

——《设计模式》GoF

2.2 模式代码

抽象工厂是工厂方法模式的子集,当要创建的对象之间有关联时才使用抽象工厂

2.2.1 问题代码

假设现在要实现SQL处理的功能

问题代码1:直接将类型硬编码到功能里,如果有多种数据库,比如MySQL、Oracle等等,就会不利于扩展

class EmployeeDAO{public:vector<EmployeeDO> GetEmployees(){SqlConnection* connection =new SqlConnection();connection->ConnectionString = "...";SqlCommand* command =new SqlCommand();command->CommandText="...";command->SetConnection(connection);SqlDataReader* reader = command->ExecuteReader();while (reader->Read()){}}
};

问题代码2:在问题代码1的基础上,使用工厂方法解决依赖具体类问题


//数据库访问有关的基类
class IDBConnection{};
class IDBConnectionFactory{
public:virtual IDBConnection* CreateDBConnection()=0;
};class IDBCommand{};
class IDBCommandFactory{
public:virtual IDBCommand* CreateDBCommand()=0;
};class IDataReader{};
class IDataReaderFactory{
public:virtual IDataReader* CreateDataReader()=0;
};//支持SQL Server
class SqlConnection: public IDBConnection{};
class SqlConnectionFactory:public IDBConnectionFactory{};class SqlCommand: public IDBCommand{};
class SqlCommandFactory:public IDBCommandFactory{};class SqlDataReader: public IDataReader{};
class SqlDataReaderFactory:public IDataReaderFactory{};//支持Oracle
class OracleConnection: public IDBConnection{};class OracleCommand: public IDBCommand{};class OracleDataReader: public IDataReader{};class EmployeeDAO{IDBConnectionFactory* dbConnectionFactory;IDBCommandFactory* dbCommandFactory;IDataReaderFactory* dataReaderFactory;public:vector<EmployeeDO> GetEmployees(){IDBConnection* connection =dbConnectionFactory->CreateDBConnection();connection->ConnectionString("...");IDBCommand* command =dbCommandFactory->CreateDBCommand();command->CommandText("...");command->SetConnection(connection); //关联性IDBDataReader* reader = command->ExecuteReader(); //关联性while (reader->Read()){}}
};

如果传递的dbConnectionFactory、dbCommandFactory、dataReaderFactory不是同一系列的就会出问题,例如一部分是MySQL对象,一部分是Oracle对象

2.2.2 重构代码


//数据库访问有关的基类
class IDBConnection{};class IDBCommand{};class IDataReader{};class IDBFactory{
public:virtual IDBConnection* CreateDBConnection()=0;virtual IDBCommand* CreateDBCommand()=0;virtual IDataReader* CreateDataReader()=0;};//支持SQL Server
class SqlConnection: public IDBConnection{};
class SqlCommand: public IDBCommand{};
class SqlDataReader: public IDataReader{};class SqlDBFactory:public IDBFactory{
public:virtual IDBConnection* CreateDBConnection()=0;virtual IDBCommand* CreateDBCommand()=0;virtual IDataReader* CreateDataReader()=0;};//支持Oracle
class OracleConnection: public IDBConnection{};class OracleCommand: public IDBCommand{};class OracleDataReader: public IDataReader{};class EmployeeDAO{IDBFactory* dbFactory;public:vector<EmployeeDO> GetEmployees(){IDBConnection* connection =dbFactory->CreateDBConnection();connection->ConnectionString("...");IDBCommand* command =dbFactory->CreateDBCommand();command->CommandText("...");command->SetConnection(connection); //关联性IDBDataReader* reader = command->ExecuteReader(); //关联性while (reader->Read()){}}
};

将一系列方法封装在一起,这便是抽象工厂模式

2.3 模式类图

在这里插入图片描述

2.4 要点总结

  • 如果没有应对“多系列对象构建”的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的工厂完全可以。
  • “系列对象”指的是在某一特定系列下的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖。
  • Abstract Factory模式主要在于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。

3. Prototype

3.1 模式介绍

原型是一种创建型设计模式,它允许您复制现有对象,而不使您的代码依赖于它们的类。

问题:
假设你有一个对象,你想创建它的一个精确副本。你会怎么做?首先,你必须创建一个相同类的新对象。然后你必须遍历原始对象的所有字段并将其值复制到新对象。

很好!但是有一个问题。并非所有对象都可以通过这种方式复制,因为某些对象的字段可能是私有的,从对象本身外部不可见。
在这里插入图片描述

直接方法还有一个问题。由于您必须知道对象的类才能创建副本,因此您的代码将依赖于该类。如果额外的依赖关系不吓到您,那么还有另一个问题。有时您只知道对象遵循的接口,但不知道其具体类,例如,当方法中的参数接受遵循某个接口的任何对象时。

3.2 模式代码


//抽象类
class ISplitter{
public:virtual void split()=0;virtual ISplitter* clone()=0; //通过克隆自己来创建对象virtual ~ISplitter(){}};//具体类
class BinarySplitter : public ISplitter{
public:virtual ISplitter* clone(){return new BinarySplitter(*this);}virtual void split(){}
};class TxtSplitter: public ISplitter{
public:virtual ISplitter* clone(){return new TxtSplitter(*this);}virtual void split(){}
};class PictureSplitter: public ISplitter{
public:virtual ISplitter* clone(){return new PictureSplitter(*this);}virtual void split(){}
};class VideoSplitter: public ISplitter{
public:virtual ISplitter* clone(){return new VideoSplitter(*this);}virtual void split(){}
};class MainForm : public Form
{ISplitter*  prototype;//原型对象public:MainForm(ISplitter*  prototype){this->prototype=prototype;}void Button1_Click(){ISplitter * splitter=prototype->clone(); //克隆原型splitter->split();}
};

3.3 模式类图

在这里插入图片描述

3.4 要点总结

  • 您可以克隆对象而不与其具体类耦合。
  • 您可以摆脱重复的初始化代码,转而克隆预先构建的原型。
  • 您可以更加方便地制作复杂的物体。
  • 处理复杂对象的配置预设时,您可以获得继承的替代方法。

4. Builder

4.1 模式介绍

动机:在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?

将一个复杂对象的构建与其表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)。

——《设计模式》GoF

4.2 模式代码

class House{//....
};class HouseBuilder {
public:House* GetResult(){return pHouse;}virtual ~HouseBuilder(){}
protected:House* pHouse;virtual void BuildPart1()=0;virtual void BuildPart2()=0;virtual void BuildPart3()=0;virtual void BuildPart4()=0;virtual void BuildPart5()=0;};class StoneHouse: public House{};class StoneHouseBuilder: public HouseBuilder{
protected:virtual void BuildPart1(){//pHouse->Part1 = ...;}virtual void BuildPart2(){}virtual void BuildPart3(){}virtual void BuildPart4(){}virtual void BuildPart5(){}};class HouseDirector{public:HouseBuilder* pHouseBuilder;HouseDirector(HouseBuilder* pHouseBuilder){this->pHouseBuilder=pHouseBuilder;}House* Construct(){pHouseBuilder->BuildPart1();for (int i = 0; i < 4; i++){pHouseBuilder->BuildPart2();}bool flag=pHouseBuilder->BuildPart3();if(flag){pHouseBuilder->BuildPart4();}pHouseBuilder->BuildPart5();return pHouseBuilder->GetResult();}
};

构建器HouseBuilder负责定义构建House时每个步骤的具体接口(变化)和管理正在构建的对象,由子类继承并实现接口;
HouseDirector负责实现House在构建时的整体流程(不变)

4.3 模式类图

在这里插入图片描述

4.4 要点总结

  • Builder 模式主要用于“分步骤构建一个复杂的对象”。在这其中“分步骤”是一个稳定的算法,而复杂对象的各个部分则经常变化。
  • 变化点在哪里,封装哪里—— Builder模式主要在于应对“复杂对象各个部分”的频繁需求变动。其缺点在于难以应对“分步骤构建算法”的需求变动。
  • 在Builder模式中,要注意不同语言中构造器内调用虚函数的差别(C++ vs. C#) 。

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

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

相关文章

SQLAlchemy系列教程:批量插入数据

高效地批量插入数据对于应用程序的性能至关重要。SQLAlchemy为批处理操作提供了几种机制&#xff0c;可以最大限度地减少开销并加快数据库事务时间。在本指南中&#xff0c;我们将探讨如何使用SQLAlchemy执行批量插入&#xff0c;包括从基础技术到高级技术。 搭建环境 在开始之…

V2X验证

1. 标准和规范验证 欧洲对 DSRC 和 V2X 系统有一系列的标准和规范,主要由 ETSI (European Telecommunications Standards Institute) 和 IEEE 等组织制定。验证通常包括以下标准和规范: ETSI EN 302 571:这是DSRC在欧洲的主要标准,规定了DSRC系统的技术要求和操作条件。ET…

openEuler系统迁移 Docker 数据目录到 /home,解决Docker 临时文件占用大问题

根据错误信息 write /var/lib/docker/tmp/...: no space left on device&#xff0c;问题的根源是 根分区&#xff08;/&#xff09;的磁盘空间不足&#xff0c;而非 /home 分区的问题。以下是详细解释和解决方案&#xff1a; 问题原因分析 Docker 临时文件占用根分区空间&…

Matlab 四分之一车辆被动悬架和模糊pid控制对比

1、内容简介 Matlab 183-四分之一车辆被动悬架和模糊pid控制对比 可以交流、咨询、答疑 2、内容说明 略 3.1 车辆多自由度模型建立 对于车辆动力学&#xff0c;一般都是研究其悬架系统&#xff0c;悬架系统由轮胎&#xff0c;轮胎空气&#xff0c;弹簧&#xff0c;减震器和…

LabVIEW旋转设备状态在线监测系统

为了提高大型旋转设备如电机和水泵的监控效率和故障诊断能力&#xff0c;用LabVIEW软件开发了一套实时监测与故障诊断系统。该系统集成了趋势分析、振动数据处理等多项功能&#xff0c;可实时分析电机电流、压力、温度及振动数据&#xff0c;以早期识别和预报故障。 ​ 项目背…

微前端 无界wujie

开发环境配置: Node.js 版本 < 18.0.0 pnpm 脚手架示例模版基于 pnpm turborepo 管理项目 如果您的当前环境中需要切换 node.js 版本, 可以使用 nvm or fnm 进行安装. 以下是通过 nvm 或者nvs 安装 Node.js 16 LTS 版本 nvs安装教程 https://blog.csdn.net/glorydx/artic…

跟网型逆变器小干扰稳定性分析与控制策略优化simulink仿真模型和代码(包含完整仿真报告)

关注&#xff1a;“电击小子程高兴的MATLAB小屋”获取巨额优惠 1.模型简介 本仿真模型基于MATLAB/Simulink&#xff08;版本MATLAB 2016Rb&#xff09;软件。建议采用matlab2016 Rb及以上版本打开。&#xff08;若需要其他版本可联系代为转换&#xff09; 近年来&#xff0c…

基于SpringBoot的“城市公交查询系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“城市公交查询系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统总体结构图 系统首页界面 用户登录界面 公…

框架源码私享笔记(02)Mybatis核心框架原理 | 一条SQL透析核心组件功能特性

最近在思考一个问题&#xff1a;如何能够更好的分享主流框架源码学习笔记&#xff08;主要是源码部分&#xff09;?让有缘刷到的同学既可以有所收获&#xff0c;还能保持对相关技术架构探讨学习热情和兴趣。以及自己也保持较高的分享热情和动力。 今天尝试用一个SQL查询作为引…

UNI-APP uts插件 支持ANDROID 监听手机状态

插件地址 https://ext.dcloud.net.cn/plugin?id22646 模块 import {startPhoneListener,stopPhoneListener,checkIsAutoRecord,toCallAutoRecorderPage,navigateToCallRecordingSettings,jumpToPermissionPage,makePhoneCall,allRecorderFilesAction,registerSmsReceiver,} f…

windows协议不再续签,华为再无windows可用,将于四月发布鸿蒙PC

大家好&#xff0c;我是国货系创始人张云泽&#xff0c;最近不少小伙伴在后台问&#xff1a;“听说Windows协议要到期了&#xff1f;我的电脑会不会变砖&#xff1f;”还有人说&#xff1a;“华为笔记本以后用不了Windows了&#xff1f;鸿蒙系统能用吗&#xff1f;”今天咱们就…

Stable Diffusion API /sdapi/v1/txt2img的完整参数列表及其说明

基本参数 {"prompt": "高质量&#xff0c;精细的恐龙", // 主提示词"negative_prompt": "模糊&#xff0c;低质量", // 负面提示词"styles": ["photorealistic", "detailed"], // 应用的风格预设&q…

TK矩阵:提高多账号管理效率的利器

随着TikTok的火爆&#xff0c;越来越多的人开始利用这个平台进行内容创作和社交互动。无论是个人创作者、品牌方&#xff0c;还是营销公司&#xff0c;TikTok都提供了巨大的机会&#xff0c;但同时也带来了运营上的挑战&#xff0c;尤其是在管理多个账户时。每个账号的维护、内…

关于Redis的集群(上)

目录 基本概念 数据分片算法 哈希求余 ​编辑一致性哈希算法 哈希槽分区算法 搭建集群环境 创建目录和配置 编写 docker-compose.yml 启动容器 构建集群 基本概念 广义的集群&#xff0c;只要是多个机器构成了分布式系统&#xff0c;都可以成为是一个“集群”。 但…

【CSS3】化神篇

目录 平面转换平移旋转改变旋转原点多重转换缩放倾斜 渐变线性渐变径向渐变 空间转换平移视距旋转立体呈现缩放 动画使现步骤animation 复合属性animation 属性拆分逐帧动画多组动画 平面转换 作用&#xff1a;为元素添加动态效果&#xff0c;一般与过渡配合使用 概念&#x…

Java 线程创建全解析:五种方式详细对比与实战示例

目录 Java 线程创建全解析&#xff1a;五种方式详细对比与实战示例一、引言二、创建线程的五种方式1. 继承 Thread 类2. 实现 Runnable 接口3. 匿名内部类实现 Thread4. 匿名内部类实现 Runnable5. 使用 Lambda 表达式&#xff08;Java 8及以上&#xff09; 三、线程创建方式对…

Dify平台离线镜像部署

Dify 是一款开源的大语言模型(LLM) 应用开发平台。它融合了后端即服务&#xff08;Backend as Service&#xff09;和 LLMOps 的理念&#xff0c;使开发者可以快速搭建生产级的生成式 AI 应用。即使你是非技术人员&#xff0c;也能参与到 AI 应用的定义和数据运营过程中。 前提…

Unitest和pytest区别

1️⃣ 设计理念 ➡️ unittest &#xff1a; 是 Python 标准库的一部分&#xff0c;设计灵感来自 Java 的 JUnit&#xff0c;强调面向对象的测试模式。 测试用例需要继承 unittest.TestCase 类&#xff0c;测试方法以 test_ 开头。 更适合传统、结构化的测试开发方式。 ⭕ pyte…

GIT标签(Tag)操作

在Git中&#xff0c;标签&#xff08;Tag&#xff09;用于标记特定的提交点&#xff0c;通常用于发布版本。 切换到需要打标签的分支&#xff1a; git checkout <branch-name>创建标签 git tag v1.0.0 git tag -a v1.0.0 -m "Release version 1.0.0"查看所…

玩转github

me github 可以给仓库添加开发人员吗 4o 是的&#xff0c;GitHub允许仓库管理员为仓库添加开发人员&#xff0c;并设置这些开发人员的角色和权限。这里是一个简单的步骤指导&#xff0c;教你如何给一个 GitHub 仓库添加开发人员&#xff1a; 前提条件 你必须有这个仓库的权限&…