C++设计模式_09_Abstract Factory 抽象工厂

与上篇介绍的Factory Method工厂方法模式一样,Abstract Factory 抽象工厂模式也属于典型的“对象创建模式”模式,解决的问题也极其相似,在理解了Factory Method工厂方法模式的基础上再去理解Abstract Factory 抽象工厂模式就会变得更加容易。

文章目录

  • 1. 动机(Motivation)
  • 2. 代码演示Factory Method工厂方法模式
    • 2.1 常规方法
    • 2.2 Factory Method工厂方法
    • 2.3 Abstract Factory 抽象工厂模式
  • 3. 模式定义
  • 4. 结构(Structure)
  • 5. 要点总结
  • 6. 其他参考

1. 动机(Motivation)

  • 在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。
  • 如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合?

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

结合代码分析什么是“一系列相互依赖的对象”

2.1 常规方法

假如有一个任务需要写一个访问的是SQL Server的数据访问层,在数据访问层需要创建一系列对象,需要创建数据库的链接SqlConnection* connection,创建数据库的命令对象SqlCommand* command,创建数据库的DataReader对象SqlDataReader* reader,这些都是跟SQL Server相关的。

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()){}}
};

但是当客户的数据库发生变化,变为Oracle、MySQL、DB2等,对应的类型就需要变化,一旦使用new就会绑死在SQL Server上,这个类就不适用于多种数据库的变化,假设需要支持多种数据库,大家第一个反应就是需要做到面向接口的编程。

2.2 Factory Method工厂方法

在上面代码的基础进行修改,结合上篇介绍的引入Factory Method工厂方法来代替new,就得到使用Factory Method工厂方法模式解决问题的代码。

//数据库访问有关的基类接口
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()){}}
};

上面的实现方法也已经解决了问题,但是暴露了什么问题

IDBConnectionFactory* dbConnectionFactory;中假如传入的是SqlConnectionFactory,但是IDBCommandFactory* dbCommandFactory;中是否可以传入OracleCommandFactory,显然是不可以的。这三个对象必须是同系列同组的,他们之间存在关联性,command和connection等之间是存在关联的。

这里就带来紊乱性,假如未来传入了不同的factory给你,出现传入SqlConnectionFactory、OracleCommandFactory、MySQLDataReaderFactory那就乱套了,无法搭配到一起,这就引出了Abstract Factory 抽象工厂模式。

2.3 Abstract Factory 抽象工厂模式

//数据库访问有关的基类
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()){}}
};

因为这3个factory特别有相关性,那用1个factory就可以了,将这3个类放在一起,实现高内聚松耦合。

大家看到这里就发现使用一个工厂,这就保证了关联性,解决了上面提到问题。从上面的代码可以看出Abstract Factory 其实被称为Family Factory更为合适,但是GOF/gaofour/4位大师已经定义了这样的名字,我们就不去计较这个名字了。

3. 模式定义

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

​ ——《设计模式》GoF

“提供一个接口”的接口就是IDBFactory,创建了IDBConnection、IDBCommand、IDataReader等

4. 结构(Structure)

在这里插入图片描述

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

在这里插入图片描述

5. 要点总结

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

第三点的意思是可以增加SQL、Oracle等系列,但是类内增加新对象就不可以了,因为SqlDBFactory是一个抽象基类,抽象基类要求稳定,这就是该模式的缺点,模式中稳定的部分就是缺点

当所有地方都变化就没有模式可以解决,当所有地方都稳定

Abstract Factory 抽象工厂模式其实跟Factory Method工厂方法模式,很接近,也可以说Factory Method工厂方法模式是Abstract Factory 抽象工厂模式的特例。

当以下代码创建三个对象

class IDBFactory{
public:virtual IDBConnection* CreateDBConnection()=0;virtual IDBCommand* CreateDBCommand()=0;virtual IDataReader* CreateDataReader()=0;};

变为创建一个对象,就是Factory Method工厂方法模式

class IDBFactory{
public:virtual IDBConnection* CreateDBConnection()=0;};

有些地方直接将Abstract Factory 抽象工厂模式和Factory Method工厂方法模式直接称作工厂模式

6. 其他参考

C++设计模式——抽象工厂模式

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

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

相关文章

docker容器健康状态健康脚本

代码&#xff1a; #!/bin/bash# 定义要监控的容器名称 container_name"mysql-container"# 使用Docker命令来检查容器的运行状态 container_status$(docker inspect --format"{{.State.Status}}" "$container_name")# 检查容器状态并进行相应操作…

uniapp实现简单的九宫格抽奖(附源码)

效果展示 uniapp实现大转盘抽奖 实现步骤&#xff1a; 1.该页面可设置8个奖品&#xff0c;每个奖品可设置中奖机会的权重&#xff0c;如下chance越大&#xff0c;中奖概率越高&#xff08;大于0&#xff09; // 示例代码 prizeList: [{id: 1,image: "https://img.alicdn…

如何注册微信小程序

如何注册微信小程序 前言 因为最近沉迷和朋友们一起下班去打麻将&#xff0c;他们推荐了一个计分的小程序&#xff0c;就不需要每局都转账或者用扑克牌记录了&#xff0c;但是这个小程序不仅打开有广告&#xff0c;各个页面都植入了广告&#xff0c;用起来十分不适。 于是我…

【Cisco】解决小米手机无法连接WIFI的问题

问题来源&#xff1a;领导的小米Note7旧手机无法连接到公司的wifi上&#xff0c;确定密码什么的都没有问题。 无线环境&#xff1a; Cisco Catalyst 9800-L Wireless Controller Cisco Catalyst 9115 AP 可能原因&#xff1a;可能是Cisco 9100系列 AP 和 Android 10设备&#…

eNSP-OSPF协议其他区域不与骨干区域相连解决方法1

OSPF多进程路由重分布 AR1 [ar1]int g0/0/0 [ar1-GigabitEthernet0/0/0]ip add 192.168.1.1 24 [ar1-GigabitEthernet0/0/0]quit [ar1]ospf [ar1-ospf-1]area 0 [ar1-ospf-1-area-0.0.0.0]net 192.168.1.0 0.0.0.255 [ar1-ospf-1-area-0.0.0.0]quit AR2 [ar2]int g0/0/0 [ar2…

微服务docker部署实战

docker基础和进阶(*已掌握的可以跳过 *) 基础 docker基础 进阶 docker进阶 准备工作 提前准备好mysql和redis的配置&#xff0c;如下 在/zzq/mysql/conf目录下配置mysql配置文件my.cnf [client] #设置客户端字符集 default_character_setutf8 [mysqld] #开启定时任务 event_s…

微信小程序4

一自定义组件应用 1.介绍 微信小程序自定义组件是指开发者可以自定义组件&#xff0c;将一些常用的 UI 元素封装成一个自定义组件&#xff0c;然后在多个页面中复用该组件&#xff0c;实现代码复用和页面性能优化的效果。 2.自定义组件分为两种类型 组件模板类型&#xff1a;…

【14】基础知识:React - redux

一、 redux理解 1、学习文档 英文文档&#xff1a;https://redux.js.org/ 中文文档&#xff1a;http://www.redux.org.cn/ Github: https://github.com/reactjs/redux 2、redux是什么 redux 是一个专门用于做状态管理的 JS 库(不是 react 插件库)。 它可以用在 react&am…

求助C语言大佬:C语言的main函数参数问题

最近在敲代码的过程中&#xff0c;突发奇想&#xff0c;产生了一个疑问&#xff1a; 为什么main函数可以任由我们定义&#xff1a;可以接收一个参数、两个参数、三个参数都接接收&#xff0c;或者可以不接收&#xff1f;这是如何实现的 int main(){retrun 0; } int main (int…

怎么使用LightPicture开源搭建图片管理系统并远程访问?【搭建私人图床】

文章目录 1.前言2. Lightpicture网站搭建2.1. Lightpicture下载和安装2.2. Lightpicture网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 现在的手机越来越先进&#xff0c;功能也越来越多&#xff0c;而手机…

TSINGSEE智慧港口可视化智能监管解决方案,助力港口码头高效监管

一、方案背景 全球经济一体化进程以及国际市场的不断融合&#xff0c;使得港口码头成为了大型货运周转中心&#xff0c;每天数以百计的大型货轮、数以千计的大型集装箱、数以万计的人员流动。港口作为货物、集装箱堆放及中转机构&#xff0c;具有昼夜不歇、天气多变、环境恶劣…

rust学习—— 控制流if 表达式

控制流 根据条件是否为真来决定是否执行某些代码&#xff0c;或根据条件是否为真来重复运行一段代码&#xff0c;是大部分编程语言的基本组成部分。Rust 代码中最常见的用来控制执行流的结构是 if 表达式和循环。 if 表达式 if 表达式允许根据条件执行不同的代码分支。你提供…

c: Queue Calling in Ubuntu

/*** file TakeNumber.h* author your name (geovindu)* brief * version 0.1* date 2023-10-20* * copyright Copyright (c) 2023 站在巨人的肩膀上 Standing on the Shoulders of Giants* */#ifndef TAKENUMBER_H #define TAKENUMBER_H#include <stdio.h> #include <…

nginx 内存管理(一)

文章目录 前提知识nginx内存管理的基础内存分配不初始化封装malloc初始化malloc 内存池内存池结构清理函数cleanup大块内存large 创建内存池申请内存void *ngx_palloc(ngx_pool_t *pool, size_t size)void *ngx_pnalloc(ngx_pool_t *pool, size_t size)void *ngx_pcalloc(ngx_p…

6 个可解锁部分 GPT-4 功能的 Chrome 扩展(无需支付 ChatGPT Plus 费用)

在过去的几个月里&#xff0c;我广泛探索了 ChatGPT 的所有可用插件。在此期间&#xff0c;我发现了一些令人惊叹的插件&#xff0c;它们改进了我使用 ChatGPT 的方式&#xff0c;但现在&#xff0c;我将透露一些您需要了解的内容。 借助 Chrome 扩展程序&#xff0c;所有 Chat…

单片机判断语句与位运算的坑

一.问题描述 在我判断Oled的某点的值是否为1时,用到了如下判断语句 if(oled[x][y/8] &1<<(y%8)但是,当我将其改为如下的判断语句,代码却跑出BUG了 if((oled[x][y/8]&1<<(y%8))1)二.原因分析 1.if语句理解错误 首选让我们看看下面的代码运行结果 #inc…

Django中ORM框架的各个操作

我们会好奇&#xff0c;python这么简洁的语言&#xff0c;数据查询是如何做的呢&#xff1f;我将进一步详细和深入地介绍Django中ORM框架的各个方面&#xff0c;包括MySQL的增删改查和复杂查询。让我们分步骤进行。 ORM框架介绍 Django的ORM框架是一个用于与数据库进行交互的工…

Stable Diffusion WebUI扩展a1111-sd-webui-tagcomplete之Booru风格Tag自动补全功能详细介绍

安装地址 直接附上地址先: Ranting8323 / A1111 Sd Webui Tagcomplete GitCodeGitCode——开源代码托管平台,独立第三方开源社区,Git/Github/Gitlabhttps://gitcode.net/ranting8323/a1111-sd-webui-tagcomplete.git上面是GitCode的地址,下面是GitHub的地址,根据自身情…

Kotlin Compose Multiplatform 跨平台开发实践之加入 iOS 支持

前言 几个月前 Compose Multiplatform 的 iOS 支持就宣布进入了 Alpha 阶段&#xff0c;这意味着它已经具备了一定的可用性。 在它发布 Alpha 的时候&#xff0c;我就第一时间尝鲜&#xff0c;但是只是浅尝辄止&#xff0c;没有做过多的探索&#xff0c;最近恰好有点时间&…

idea Java代码格式化规范

文章目录 引入基础知识代码模板idea模板eclipse模板1.安装插件2.生成配置文件3.导入配置文件 附录一&#xff1a;xml配置项说明附录二&#xff1a;赠送 引入 最近在公司开发中&#xff0c;遇到了一点小问题&#xff0c;组内各同事的格式化规范不一致。一来导致代码样式并不统一…