当前位置: 首页 > 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;不同就输出最长的那个字符串长度 思路分析 数据输入存值之后&…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj&#xff0c;再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...