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

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;往往存在更多系列对象的创建工作。 假设案例 假设…...

python-leetcode-路径总和

112. 路径总和 - 力扣&#xff08;LeetCode&#xff09; # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.right right class Solution:de…...

WGCLOUD使用介绍 - 如何监控ActiveMQ和RabbitMQ

根据WGCLOUD官网的信息&#xff0c;目前没有针对ActiveMQ和RabbitMQ这两个组件专门做适配 不过可以使用WGCLOUD已经具备的通用监测模块&#xff1a;进程监测、端口监测或者日志监测、接口监测 来对这两个组件进行监控...

智能汽车网络安全威胁报告

近年来随着智能汽车技术的快速发展&#xff0c;针对智能汽车的攻击也逐渐从传统的针对单一车辆控制器的攻击转变为针对整车智能化服务的攻击&#xff0c;包括但不限于对远程控制应用程序的操控、云服务的渗透、智能座舱系统的破解以及对第三方应用和智能服务的攻击。随着WP.29 …...

WPS怎么使用latex公式?

1、下载并安装mathtype https://blog.csdn.net/weixin_43135178/article/details/125143654?sharetypeblogdetail&sharerId125143654&sharereferPC&sharesourceweixin_43135178&spm1011.2480.3001.8118 2、将mathtype嵌入在WPS MathType面板嵌入器,免费工具…...

Cyber Security 101-Build Your Cyber Security Career-Security Principles(安全原则)

了解安全三元组以及常见的安全模型和原则。 任务1&#xff1a;介绍 安全已成为一个流行词;每家公司都想声称其产品或服务是安全的。但事实真的如此吗&#xff1f; 在我们开始讨论不同的安全原则之前&#xff0c;了解我们正在保护资产的对手至关重要。您是否试图阻止蹒跚学步…...

Formality:时序变换(二)(不可读寄存器移除)

相关阅读 Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 一、引言 时序变换在Design Compiler的首次综合和增量综合中都可能发生&#xff0c;它们包括&#xff1a;时钟门控(Clock Gating)、寄存器合并(Register Merging)、…...

MathType下载与安装详细教程

MathType 软件简介安装步骤重新嵌入word 软件简介 数学公式编辑器MathType 是一款专业的数学公式编辑工具&#xff0c;理科生专用的工具。MathType公式编辑器能够帮助用户在各种文档中插入复杂的数学公式和符号。数学公式编辑器工具可以轻松输入各种复杂的公式和符号&#xff…...

docker中运行的MySQL怎么修改密码

1&#xff0c;进入MySQL容器 docker exec -it 容器名 bash 我运行了 docker ps命令查看。正在运行的容器名称。可以看到MySQL的我起名为db docker exec -it db bash 这样就成功的进入到容器中了。 2&#xff0c;登录MySQL中 mysql -u 用户名 -p 回车 密码 mysql -u root -p roo…...

内外网文件摆渡企业常见应用场景和对应方案

在如今的企业环境中&#xff0c;内外网文件摆渡的需求越来越常见&#xff0c;也变得越来越重要。随着信息化的不断推进&#xff0c;企业内部和外部之间的数据交换越来越频繁&#xff0c;如何安全、高效地进行文件传输成了一个关键问题。今天&#xff0c;咱就来聊聊内外网文件摆…...

【Block总结】PKI 模块,无膨胀多尺度卷积,增强特征提取的能力|即插即用

论文信息 标题: Poly Kernel Inception Network for Remote Sensing Detection 作者: Xinhao Cai, Qiuxia Lai, Yuwei Wang, Wenguan Wang, Zeren Sun, Yazhou Yao 论文链接&#xff1a;https://arxiv.org/pdf/2403.06258 代码链接&#xff1a;https://github.com/NUST-Mac…...

自制一个入门STM32 四足机器人具体开发顺序

0 前期准备 1. 知识储备 学习 STM32 微控制器的基础知识&#xff0c;包括 GPIO、定时器、串口通信等外设的使用&#xff0c;可通过官方文档、教程和视频课程进行学习。了解舵机控制原理&#xff0c;因为四足机器人通常使用舵机来实现关节运动。掌握基本的机械结构设计知识&am…...

物联网智能项目之——智能家居项目的实现!

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于物联网智能项目之——智能家居项目…...

[免费]微信小程序智能商城系统(uniapp+Springboot后端+vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序智能商城系统(uniappSpringboot后端vue管理端)&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序智能商城系统(uniappSpringboot后端vue管理端) Java毕业设计_哔哩哔哩_bilibili 项目介绍…...

C28.【C++ Cont】顺序表的实现

&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;初二篇&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8; 目录 1.知识回顾…...

【电工基础】低压电器元件,低压断路器(空开QF),接触器(KM)

一.低压电器元件定义 电器可分为高压电器和低压电器两大类&#xff0c;我国现行标准是将工作在交流1200V(50Hz)以下、直流1500V以下的电器设备称为低压电器。 二.低压断路器&#xff0c;空开&#xff0c;空气断路器 1.空开图片与使用方式 当电路中发生严重过载、短路及失压等故…...

从 UTC 日期时间字符串获取 Unix 时间戳:C 和 C++ 中的挑战与解决方案

在编程世界里&#xff0c;从 UTC 日期时间字符串获取 Unix 时间戳&#xff0c;看似简单&#xff0c;实则暗藏玄机。你以为输入一个像 “Fri, 17 Jan 2025 06:07:07” 这样的 UTC 时间&#xff0c;然后轻松得到 1737094027&#xff08;从 1970 年 1 月 1 日 00:00:00 UTC 开始经…...

[前端开发]记录国内快速cdn库,用于在线引入JavaScript第三方库

字节跳动的两个库,官网地址如下,搜索时优先找第一个,可用来链接axios,Boostrap等等第三方库 1. 字节跳动静态资源公共库 比如说搜索lodash,用于节流防抖的库,点击复制即可,一般是****.js或****.min.js这样的为后缀名的链接 点击复制即可, <script src"https://lf9-cd…...

留学生scratch计算机haskell函数ocaml编程ruby语言prolog作业VB

您列出了一系列编程语言和技术&#xff0c;这些可能是您在留学期间需要学习或完成作业的内容。以下是对每个项目的简要说明和它们可能涉及的领域或用途&#xff1a; Scratch&#xff1a; Scratch是一种图形化编程语言&#xff0c;专为儿童和初学者设计&#xff0c;用于教授编程…...

CF 766A.Mahmoud and Longest Uncommon Subsequence(Java实现)

题目分析 (小何同学语文不太好&#xff0c;看这个题弯弯绕绕&#xff0c;看不懂一点&#xff0c;哈哈哈。)在尝试示例中分析之后&#xff0c;题目的意思大概就是&#xff0c;两个字符串相同就输出-1&#xff0c;不同就输出最长的那个字符串长度 思路分析 数据输入存值之后&…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...