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

python-leetcode-路径总和
112. 路径总和 - 力扣(LeetCode) # 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官网的信息,目前没有针对ActiveMQ和RabbitMQ这两个组件专门做适配 不过可以使用WGCLOUD已经具备的通用监测模块:进程监测、端口监测或者日志监测、接口监测 来对这两个组件进行监控...

智能汽车网络安全威胁报告
近年来随着智能汽车技术的快速发展,针对智能汽车的攻击也逐渐从传统的针对单一车辆控制器的攻击转变为针对整车智能化服务的攻击,包括但不限于对远程控制应用程序的操控、云服务的渗透、智能座舱系统的破解以及对第三方应用和智能服务的攻击。随着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:介绍 安全已成为一个流行词;每家公司都想声称其产品或服务是安全的。但事实真的如此吗? 在我们开始讨论不同的安全原则之前,了解我们正在保护资产的对手至关重要。您是否试图阻止蹒跚学步…...

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

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

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

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

【Block总结】PKI 模块,无膨胀多尺度卷积,增强特征提取的能力|即插即用
论文信息 标题: Poly Kernel Inception Network for Remote Sensing Detection 作者: Xinhao Cai, Qiuxia Lai, Yuwei Wang, Wenguan Wang, Zeren Sun, Yazhou Yao 论文链接:https://arxiv.org/pdf/2403.06258 代码链接:https://github.com/NUST-Mac…...
自制一个入门STM32 四足机器人具体开发顺序
0 前期准备 1. 知识储备 学习 STM32 微控制器的基础知识,包括 GPIO、定时器、串口通信等外设的使用,可通过官方文档、教程和视频课程进行学习。了解舵机控制原理,因为四足机器人通常使用舵机来实现关节运动。掌握基本的机械结构设计知识&am…...

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

[免费]微信小程序智能商城系统(uniapp+Springboot后端+vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序智能商城系统(uniappSpringboot后端vue管理端),分享下哈。 项目视频演示 【免费】微信小程序智能商城系统(uniappSpringboot后端vue管理端) Java毕业设计_哔哩哔哩_bilibili 项目介绍…...

C28.【C++ Cont】顺序表的实现
🧨🧨🧨🧨🧨🧨🧨🧨🧨初二篇🧨🧨🧨🧨🧨🧨🧨🧨🧨 目录 1.知识回顾…...

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

从 UTC 日期时间字符串获取 Unix 时间戳:C 和 C++ 中的挑战与解决方案
在编程世界里,从 UTC 日期时间字符串获取 Unix 时间戳,看似简单,实则暗藏玄机。你以为输入一个像 “Fri, 17 Jan 2025 06:07:07” 这样的 UTC 时间,然后轻松得到 1737094027(从 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
您列出了一系列编程语言和技术,这些可能是您在留学期间需要学习或完成作业的内容。以下是对每个项目的简要说明和它们可能涉及的领域或用途: Scratch: Scratch是一种图形化编程语言,专为儿童和初学者设计,用于教授编程…...

CF 766A.Mahmoud and Longest Uncommon Subsequence(Java实现)
题目分析 (小何同学语文不太好,看这个题弯弯绕绕,看不懂一点,哈哈哈。)在尝试示例中分析之后,题目的意思大概就是,两个字符串相同就输出-1,不同就输出最长的那个字符串长度 思路分析 数据输入存值之后&…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统
Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...
TJCTF 2025
还以为是天津的。这个比较容易,虽然绕了点弯,可还是把CP AK了,不过我会的别人也会,还是没啥名次。记录一下吧。 Crypto bacon-bits with open(flag.txt) as f: flag f.read().strip() with open(text.txt) as t: text t.read…...

react菜单,动态绑定点击事件,菜单分离出去单独的js文件,Ant框架
1、菜单文件treeTop.js // 顶部菜单 import { AppstoreOutlined, SettingOutlined } from ant-design/icons; // 定义菜单项数据 const treeTop [{label: Docker管理,key: 1,icon: <AppstoreOutlined />,url:"/docker/index"},{label: 权限管理,key: 2,icon:…...