当前位置: 首页 > article >正文

7.抽象工厂(Abstract Factory)

抽象工厂与工厂方法极其类似,都是绕开new的,但是有些许不同。

动机

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

假设案例

假设现在有这样一个需求,我们需要做一个数据访问层。
在数据访问层需要创建一系列的对象,比如建立链接,创建数据库的命令对象,创建数据库的DataReader对象。但是数据库可能不只有Sql的,可能还有别的类型的。

class EmployeeDAO{public:vector<EmployeeDO> GetEmployees(){// sql 链接SqlConnection* connection = new SqlConnection();// 链接字符串connection->ConnectionString = "...";// sql 命令SqlCommand* command = new SqlCommand();command->CommandText="...";// 设置链接command->SetConnection(connection);// 执行读取SqlDataReader* reader = command->ExecuteReader();while (reader->Read()){}}
};

当前代码实现是 紧耦合:
EmployeeDAO 类与具体的数据库实现(如 SQL Server)紧密耦合,难以切换到其他数据库(如 MySQL、Oracle 等)。


//数据库访问有关的基类
class IDBConnection{};// IDB 连接工厂
class IDBConnectionFactory{
public:virtual IDBConnection* CreateDBConnection()=0;
};// IDB 命令基类
class IDBCommand{};
// IDB 命令工厂
class IDBCommandFactory{
public:virtual IDBCommand* CreateDBCommand()=0;
};// IData 数据阅读器
class IDataReader{};// IData 数据阅读器工厂
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()){}}
};

按照之前的工厂模式,编写代码:

//数据库访问有关的基类
class IDBConnection {};// IDB 连接工厂
class IDBConnectionFactory {
public:virtual IDBConnection* CreateDBConnection() = 0;
};// IDB 命令基类
class IDBCommand {};
// IDB 命令工厂
class IDBCommandFactory {
public:virtual IDBCommand* CreateDBCommand() = 0;
};// IData 数据阅读器
class IDataReader {};// IData 数据阅读器工厂
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()) {}}
};

在这里插入图片描述
上述方法确实能够解决new的问题,但是也存在一些其它的问题。就是一开始创建的三个工厂必须是同系列的,具备关联性。解决这个问题就需要引入抽象工厂。

首先,定义数据库操作的抽象接口:

// 数据库连接接口
class IDbConnection {
public:virtual void SetConnectionString(const std::string& connStr) = 0;virtual void Open() = 0;virtual void Close() = 0;virtual ~IDbConnection() {}
};// 数据库命令接口
class IDbCommand {
public:virtual void SetCommandText(const std::string& commandText) = 0;virtual void SetConnection(IDbConnection* connection) = 0;virtual IDbDataReader* ExecuteReader() = 0;virtual ~IDbCommand() {}
};// 数据读取器接口
class IDbDataReader {
public:virtual bool Read() = 0;virtual std::string GetString(int index) = 0;virtual int GetInt(int index) = 0;virtual ~IDbDataReader() {}
};

为每种数据库实现具体的类。例如,SQL Server 的实现:

// SQL Server 连接
class SqlConnection : public IDbConnection {
public:void SetConnectionString(const std::string& connStr) override {// 设置连接字符串}void Open() override {// 打开连接}void Close() override {// 关闭连接}
};// SQL Server 命令
class SqlCommand : public IDbCommand {
private:std::string commandText;IDbConnection* connection;
public:void SetCommandText(const std::string& commandText) override {this->commandText = commandText;}void SetConnection(IDbConnection* connection) override {this->connection = connection;}IDbDataReader* ExecuteReader() override {// 执行命令并返回读取器return new SqlDataReader();}
};// SQL Server 数据读取器
class SqlDataReader : public IDbDataReader {
public:bool Read() override {// 读取下一行return true;}std::string GetString(int index) override {// 获取字符串数据return "data";}int GetInt(int index) override {// 获取整数数据return 123;}
};

定义一个抽象工厂接口,用于创建数据库相关的对象:

class IDbFactory {
public:virtual IDbConnection* CreateConnection() = 0;virtual IDbCommand* CreateCommand() = 0;virtual IDbDataReader* CreateDataReader() = 0;virtual ~IDbFactory() {}
};

为每种数据库实现具体的工厂类。例如,SQL Server 的工厂:

class SqlDbFactory : public IDbFactory {
public:IDbConnection* CreateConnection() override {return new SqlConnection();}IDbCommand* CreateCommand() override {return new SqlCommand();}IDbDataReader* CreateDataReader() override {return new SqlDataReader();}
};

将 EmployeeDAO 类改为依赖抽象工厂,而不是具体的数据库实现:

class EmployeeDAO {
private:IDbFactory* dbFactory;
public:EmployeeDAO(IDbFactory* factory) : dbFactory(factory) {}std::vector<EmployeeDO> GetEmployees() {std::vector<EmployeeDO> employees;// 创建连接IDbConnection* connection = dbFactory->CreateConnection();connection->SetConnectionString("...");connection->Open();// 创建命令IDbCommand* command = dbFactory->CreateCommand();command->SetCommandText("SELECT * FROM Employees");command->SetConnection(connection);// 执行命令并读取数据IDbDataReader* reader = command->ExecuteReader();while (reader->Read()) {EmployeeDO employee;employee.name = reader->GetString(0);employee.age = reader->GetInt(1);employees.push_back(employee);}// 释放资源delete reader;delete command;delete connection;return employees;}
};

使用示例,在客户端代码中,通过工厂创建 EmployeeDAO 对象

int main() {// 创建 SQL Server 工厂IDbFactory* sqlFactory = new SqlDbFactory();// 创建 EmployeeDAO 对象EmployeeDAO dao(sqlFactory);// 获取员工数据std::vector<EmployeeDO> employees = dao.GetEmployees();// 释放工厂delete sqlFactory;return 0;
}
改进后的优点
解耦:EmployeeDAO 类不再依赖具体的数据库实现,而是依赖抽象接口,符合依赖倒置原则。扩展性:如果需要支持新的数据库类型,只需实现新的工厂类和数据库类,无需修改 EmployeeDAO 类。可测试性:可以通过模拟工厂和数据库对象,轻松进行单元测试。符合开闭原则:系统对扩展开放,对修改关闭。

在这里插入图片描述

模式定义

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

要点总结

  • 如果没有应对“多系列对象构建”的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的工厂完全可以。

  • “系列对象”指的是在某一特定系列下的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖。

  • Abstract Factory模式主要在于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。(因为如果要加新对象的话,就会改变抽象基类IDBFactory)

相关文章:

7.抽象工厂(Abstract Factory)

抽象工厂与工厂方法极其类似&#xff0c;都是绕开new的&#xff0c;但是有些许不同。 动机 在软件系统中&#xff0c;经常面临着“一系列相互依赖的对象”的创建工作&#xff1b;同时&#xff0c;由于需求的变化&#xff0c;往往存在更多系列对象的创建工作。 假设案例 假设…...

Android-音频采集

前言 音视频这块&#xff0c;首先是要先采集音频。今天我们就来深入探讨一下 Android 音频采集的两大类型&#xff1a;Mic 音频采集和系统音频采集。 Mic音频采集 Android提供了两个API用于实现录音功能&#xff1a;android.media.AudioRecord、android.media.MediaRecorder。…...

旋钮屏设备物联网方案,ESP32-C3无线通信应用,助力设备智能化升级

在智能家居的浪潮中&#xff0c;旋钮屏以其独特的交互方式和便捷的操作体验&#xff0c;逐渐成为智能家电控制面板上的新宠儿。从智能冰箱、洗衣机到烤箱、空气炸锅等设备&#xff0c;旋钮屏的应用无处不在。 通过简单的旋转和按压操作&#xff0c;用户可以轻松调节温度、时间…...

DRGDIP 2.0时代下基于PostgreSQL的成本管理实践与探索(上)

一、引言 1.1 研究背景与意义 在医疗领域的改革进程中&#xff0c; DRG/DIP 2.0 时代&#xff0c;医院成本管理的重要性愈发凸显。新的医保支付方式下&#xff0c;医院的收入不再单纯取决于医疗服务项目的数量&#xff0c;而是与病种的分组、费用标准以及成本控制紧密相关。这…...

游戏引擎 Unity - Unity 打开项目、Unity Editor 添加简体中文语言包模块、Unity 项目设置为简体中文

Unity Unity 首次发布于 2005 年&#xff0c;属于 Unity Technologies Unity 使用的开发技术有&#xff1a;C# Unity 的适用平台&#xff1a;PC、主机、移动设备、VR / AR、Web 等 Unity 的适用领域&#xff1a;开发中等画质中小型项目 Unity 适合初学者或需要快速上手的开…...

edu小程序挖掘严重支付逻辑漏洞

edu小程序挖掘严重支付逻辑漏洞 一、敏感信息泄露 打开购电小程序 这里需要输入姓名和学号&#xff0c;直接搜索引擎搜索即可得到&#xff0c;这就不用多说了&#xff0c;但是这里的手机号可以任意输入&#xff0c;只要用户没有绑定手机号这里我们输入自己的手机号抓包直接进…...

安卓/鸿蒙模拟位置信息-Fake Location模拟虚拟定位打卡

一、软件下载安装 需要用到的软件就一个即&#xff1a;FakeLocation虚拟打卡定位 下载地址&#xff1a;FakeLocation虚拟打卡定位.app 二、手机端设置 打开手机设置-关于手机-版本信息-版本号&#xff0c;连续点击版本号直到出现已进入开发者模式字样&#xff0c;此时打开手…...

(一)DeepSeek大模型安装部署-Ollama安装

大模型deepseek安装部署 (一)、安装ollama curl -fsSL https://ollama.com/install.sh | sh sudo systemctl start ollama sudo systemctl enable ollama sudo systemctl status ollama(二)、安装ollama遇到网络问题&#xff0c;请手动下载 ollama-linux-amd64.tgz curl -L …...

LabVIEW2025中文版软件安装包、工具包、安装教程下载

下载链接&#xff1a;LabVIEW及工具包大全-三易电子工作室http://blog.eeecontrol.com/labview6666 《LabVIEW2025安装图文教程》 1、解压后&#xff0c;双击install.exe安装 2、选中“我接受上述2条许可协议”&#xff0c;点击下一步 3、点击下一步&#xff0c;安装NI Packa…...

Rust 再谈泛型

第一章&#xff1a;Trait约束 - 变形许可证系统 1.1 正面案例&#xff1a;持证上岗的变形金刚 trait Transform {fn transform(&self) -> String; }struct Car {model: String }impl Transform for Car {fn transform(&self) -> String {format!("{}变形为…...

chrome插件模板;使用 React 18 和 Webpack 5 的 Chrome 扩展样板

一、软件介绍&#xff08;文末提供下载&#xff09; 这是一个基本的 Chrome 扩展样板&#xff0c;可帮助您编写模块化和现代的 Javascript 代码&#xff0c;轻松加载 CSS&#xff0c;并在代码更改时自动重新加载浏览器。 github地址&#xff1a;https://github.com/lxieyang/c…...

解锁.NET Fiddle:在线编程的神奇之旅

在.NET 开发的广袤领域中&#xff0c;快速验证想法、测试代码片段以及便捷地分享代码是开发者们日常工作中不可或缺的环节。而.NET Fiddle 作为一款卓越的在线神器&#xff0c;正逐渐成为众多.NET 开发者的得力助手。它打破了传统开发模式中对本地开发环境的依赖&#xff0c;让…...

AI大模型:DeepSeek

近期DeepSeek产生了很大的影响力。首先来自于性能,给了业内一个很好的释放,缓解了HPC以及大规模集群被卡的焦虑。通过实验证实了小规模团队(公开资料显示规模约150左右)在资源受限的情况下(2M H100 GPU时),依然可以完成对领先大模型的实现与部署。后续观察该团队是否可以…...

Spring MVC ONE

第一章&#xff1a;Java web的发展历史 一.Model I和Model II 1.Model I开发模式 Model1的开发模式是&#xff1a;JSPJavaBean的模式&#xff0c;它的核心是Jsp页面&#xff0c;在这个页面中&#xff0c;Jsp页面负责整合页面和JavaBean&#xff08;业务逻辑&#xff09;&…...

【Git】一、初识Git Git基本操作详解

文章目录 学习目标Ⅰ. 初始 Git&#x1f4a5;注意事项 Ⅱ. Git 安装Linux-centos安装Git Ⅲ. Git基本操作一、创建git本地仓库 -- git init二、配置 Git -- git config三、认识工作区、暂存区、版本库① 工作区② 暂存区③ 版本库④ 三者的关系 四、添加、提交更改、查看提交日…...

SQL 秒变三线表 sql导出三线表

&#x1f3af;SQL 秒变三线表&#xff0c;校园小助手超神啦 宝子们&#xff0c;搞数据分析、写论文的时候&#xff0c;从 SQL 里导出数据做成三线表是不是特别让人头疼&#x1f629; 手动调整格式&#xff0c;不仅繁琐&#xff0c;还容易出错&#xff0c;分分钟把人逼疯&#…...

PySpark学习笔记5-SparkSQL

sparkSql的数据抽象有两种。 一类是data set适用于java和Scala 一类是data frame适用于java&#xff0c;Scala&#xff0c;python 将r d d转换为data frame #方式一 df spark.createDataFrame(rdd,schema[name,age]) #方式二 schema Structtype(). add(id,integertype(),nu…...

ES6 字符串、数值、数组扩展使用总结

1. 字符串的扩展方法 1.1 includes() // 判断字符串是否包含指定字符串 const str Hello World; console.log(str.includes(Hello)); // true console.log(str.includes(hello)); // false console.log(str.includes(World, 6)); // true - 从位置6开始搜索// 实际应用 func…...

项目测试之Postman

文章目录 基础实战进行批量测试并输出报告 基础 实战 进行批量测试并输出报告 参考&#xff1a; https://blog.csdn.net/tyh_keephunger/article/details/109205191 概述 Newman是什么&#xff1f;Newman是Postman的命令行工具&#xff0c;用于执行接口测试集合。操作过程…...

C#常用744单词

1.visual 可见的 2.studio 工作室 3.dot 点 4.net 网 5.harp 尖端的&#xff0c;锋利的。 6.amework 骨架&#xff0c;构架&#xff0c;框架 7.beta 测试版&#xff0c;试用版 8.XML&#xff08;全称&#xff1a;eXtensible Markup Language&#xff09…...

如何利用maven更优雅的打包

最近在客户现场部署项目&#xff0c;有两套环境&#xff0c;无法连接互联网&#xff0c;两套环境之间也是完全隔离&#xff0c;于是问题就来了&#xff0c;每次都要远程到公司电脑改完代码&#xff0c;打包&#xff0c;通过网盘&#xff08;如果没有会员&#xff0c;上传下载慢…...

图像分类与目标检测算法

在计算机视觉领域&#xff0c;图像分类与目标检测是两项至关重要的技术。它们通过对图像进行深入解析和理解&#xff0c;为各种应用场景提供了强大的支持。本文将详细介绍这两项技术的算法原理、技术进展以及当前的落地应用。 一、图像分类算法 图像分类是指将输入的图像划分为…...

arm 下 多线程访问同一变量 ,使用原子操作 性能差问题

arm下原子操作性能差的原因 Linux Kernel(armv8-aarch64) 的原子操作的底层实现 - 极术社区 - 连接开发者与智能计算生态 arm 下如何解决 ARMs LSE (for atomics) and MySQL – MySQL On ARM – All you need to know about MySQL (and its variants) on ARM. arm 下lse 和…...

HAL库 Systick定时器 基于STM32F103EZT6 野火霸道,可做参考

目录 1.时钟选择(这里选择高速外部时钟) ​编辑 2.调试模式和时基源选择: 3.LED的GPIO配置 这里用板子的红灯PB5 4.工程配置 5.1ms的systick中断实现led闪烁 源码: 6.修改systick的中断频率 7.systick定时原理 SysTick 定时器的工作原理 中断触发机制 HAL_SYSTICK_Co…...

Spring Boot常用注解深度解析:从入门到精通

今天&#xff0c;这篇文章带你将深入理解Spring Boot中30常用注解&#xff0c;通过代码示例和关系图&#xff0c;帮助你彻底掌握Spring核心注解的使用场景和内在联系。 一、启动类与核心注解 1.1 SpringBootApplication 组合注解&#xff1a; SpringBootApplication Confi…...

【基于SprintBoot+Mybatis+Mysql】电脑商城项目之用户注册

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【计算机网络】【Mybatis篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 目录 &#x1f3af;项目基本介绍 &#x1f6a6;项…...

亚远景-从SPICE到ASPICE:汽车软件开发的标准化演进

一、SPICE标准的起源与背景 SPICE&#xff0c;全称“Software Process Improvement and Capability dEtermination”&#xff0c;即“软件流程改进和能力测定”&#xff0c;是由国际标准化组织ISO、国际电工委员会IEC、信息技术委员会JTC1联合发起制定的ISO 15504标准。该标准旨…...

力扣1022. 从根到叶的二进制数之和(二叉树的遍历思想解决)

Problem: 1022. 从根到叶的二进制数之和 文章目录 题目描述思路复杂度Code 题目描述 思路 遍历思想(利用二叉树的先序遍历) 1.在先序遍历的过程中&#xff0c;用一个变量path记录并更新其经过的路径上的值&#xff0c;当遇到根节点时再将其加到结果值res上&#xff1b; 2.该题…...

Redis背景介绍

⭐️前言⭐️ 本文主要做Redis相关背景介绍&#xff0c;包括核心能力、重要特性和使用场景。 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f349;博主将持续更新学习记录收获&#xff0c;友友们有任何问题可以在评论区留言 &#x1f349;博客中涉及源码及博主…...

LabVIEW图像采集与应变场测量系统

开发了一种基于LabVIEW的图像采集与应变场测量系统&#xff0c;提供一种高精度、非接触式的测量技术&#xff0c;用于监测物体的全场位移和应变。系统整合了实时监控、数据记录和自动对焦等功能&#xff0c;适用于工程应用和科学研究。 项目背景 传统的位移和应变测量技术往往…...