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

news/2025/2/4 23:10:49/

与上篇介绍的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.ppmy.cn/news/1165780.html

相关文章

获取电商订单接口需要哪些资质呢?

获取电商订单接口需要哪些资质呢&#xff1f;请看下面讲解 获取电商订单接口有多种方式&#xff0c;如果通过电商平台的开放平台获取电商订单接口&#xff0c;在入驻开放平台时便需要一定的资质。 那就以淘宝开放平台为例&#xff0c;有不同的接入模式可供选择&#xff0c;分…

C# 给List编个序号

给List编个号 int[] numbers { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };int i 0;var q (from n in numbersselect (index:i,number:n)).ToList();foreach (var v in q) {Console.WriteLine($"i {v.index}, v {v.number}"); }

初识Java 14-2 测试

目录 测试驱动开发&#xff08;TDD&#xff09; 日志 调试 使用JDB进行调试 基准测试 微基准测试 Java微基准测试工具&#xff08;JMH&#xff09; 分析和优化 重构 本笔记参考自&#xff1a; 《On Java 中文版》 测试驱动开发&#xff08;TDD&#xff09; 测试驱动开…

web前端基础CSS------美化页面“footer”部分

一&#xff0c;实验代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>关于我们</title><style type"text/css">#footer{margin: 10px 0px;background: #f5f5f5;border: top 1px solid #eee ;}#f…

旧版Mac如何装新系统

macOS Ventura 最低系统需要&#xff0c;17年序列电脑。老电脑15年的&#xff0c;无法安装新系统。使用方法直接采用大佬方法 一.在GitHub下载 OpenCore、Hackintool OpenCore 用来修改系统的机型&#xff0c;修改后可直接在软件更新中更新macOS Ventura。 Hackintool 用来生…

vue2升级到vue2.7

vue2升级到vue2.7 小小的改进,大大的提升 只需要简单修改,开发体验得到大大提升. 为什么要升级Vue2.7 不能拒绝的理由: 组合式 API(解决mixins问题:命名冲突,隐式依赖)单文件组件内的 <script setup>语法模板表达式中支持 ESNext 语法(可选链:?.、空值合并:??)单文…

实用API管理平台推荐:Apipost

在数字化时代&#xff0c;API已成为企业和开发者实现数据互通、应用集成的重要桥梁。然而&#xff0c;随着API数量的不断增加&#xff0c;API设计、调试、文档和测试等工作也变得越来越复杂。为了解决这一痛点&#xff0c;一款名为Apipost的API协同研发工具应运而生&#xff0c…

前端常见的数据类型有哪些?

在前端开发中,常见的数据类型包括: 1:字符串(String):表示文本数据,用引号(单引号或双引号)括起来,例如:“Hello, World!”。 创建字符串:let str = ‘Hello, World!’;获取字符串长度:let length = str.length;字符串拼接:let newStr = str1 + str2;2:数字(N…