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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/961940.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

电路研究9.2.3——合宙Air780EP中FTP——FTPGET 命令使用方法研究

怎么说呢&#xff0c;之前也是看的&#xff0c;但是也很迷茫&#xff0c;感觉上虽然是对的&#xff0c;但是无法联系到应用里面&#xff0c;今天研究一下FTP 命令使用方法吧。 15.29 使用方法举例 这里发现下面那些看的不懂呢&#xff0c;于是就返回FTP的应用了。 9.5.4 FTP 应…

高精度加法乘法

高精度加法&乘法都是把数字转化成数组进行运算&#xff0c;存储 高精度加法 建议多在纸上画画&#xff0c;梳理思路 代码实现 输入字符串 //初始化数组存储 int a[250]{0}; int b[250]{0}; int c[251]{0}; //定义字符串&#xff0c;输入字符串 string s1,s2; getline(c…

【C++】STL介绍 + string类使用介绍 + 模拟实现string类

目录 前言 一、STL简介 二、string类 1.为什么学习string类 2.标准库中的string类 3.auto和范围for 4.迭代器 5.string类的常用接口说明 三、模拟实现 string类 前言 本文带大家入坑STL&#xff0c;学习第一个容器string。 一、STL简介 在学习C数据结构和算法前&#xff0c;我…

数据结构的队列

一.队列 1.队列&#xff08;Queue&#xff09;的概念就是先进先出。 2.队列的用法&#xff0c;红色框和绿色框为两组&#xff0c;offer为插入元素&#xff0c;poll为删除元素&#xff0c;peek为查看元素红色的也是一样的。 3.LinkedList实现了Deque的接口&#xff0c;Deque又…

【开源免费】基于SpringBoot+Vue.JS体育馆管理系统(JAVA毕业设计)

本文项目编号 T 165 &#xff0c;文末自助获取源码 \color{red}{T165&#xff0c;文末自助获取源码} T165&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

LabVIEW温度修正部件测试系统

LabVIEW温度修正部件测试系统 这个基于LabVIEW的温度修正部件测试系统旨在解决飞行器温度测量及修正电路的测试需求。该系统的意义在于提供一个可靠的测试平台&#xff0c;用于评估温度修正部件在实际飞行器环境中的性能表现&#xff0c;从而确保飞行器的安全性和可靠性。 系统…

vim的特殊模式-可视化模式

可视化模式&#xff1a;按 v进入可视化模式 选中 y复制 d剪切/删除 可视化块模式: ctrlv 选中 y复制 d剪切/删除 示例&#xff1a; &#xff08;vim可视化模式的进阶使用&#xff1a;vim可视化模式的进阶操作-CSDN博客&#xff09;

mysql重学(一)mysql语句执行流程

思考 一条查询语句如何执行&#xff1f;mysql语句中若列不存在&#xff0c;则在哪个阶段报错一条更新语句如何执行&#xff1f;redolog和binlog的区别&#xff1f;为什么要引入WAL什么是Changbuf&#xff1f;如何工作写缓冲一定好吗&#xff1f;什么情况会引发刷脏页删除语句会…

【Docker】Docker入门了解

文章目录 Docker 的核心概念Docker 常用命令示例&#xff1a;构建一个简单的 C 应用容器1. 创建 C 应用2. 创建 Dockerfile3. 构建镜像4. 运行容器 Docker 优势学习 Docker 的下一步 **一、Docker 是什么&#xff1f;****为什么 C 开发者需要 Docker&#xff1f;** **二、核心概…

使用langchain ollama gradio搭建一个本地基于deepseek r1的RAG问答系统

目录 简介 环境配置 具体实现 安装依赖 定义模型和prompt 加载检索文档 切割 向量存储 创建检索器 实例化 前端搭建 实现效果 小tips 简介 首先介绍一下使用的几个工具&#xff0c;模型和rag的步骤&#xff0c;注&#xff1a;这里只是简单描述一下&#xff0c;不展…

Python中的函数(下)

函数返回值 返回单个值 函数可以通过 return 语句返回一个值。一旦执行到 return 语句&#xff0c;函数就会停止执行&#xff0c;并将指定的值返回给调用者。例如&#xff1a; 返回多个值 实际上&#xff0c;Python函数只能返回一个值&#xff0c;但可以通过返回一个元组来模…

Spring Boot + Facade Pattern : 通过统一接口简化多模块业务

文章目录 Pre概述在编程中&#xff0c;外观模式是如何工作的&#xff1f;外观设计模式 UML 类图外观类和子系统的关系优点案例外观模式在复杂业务中的应用实战运用1. 项目搭建与基础配置2. 构建子系统组件航班服务酒店服务旅游套餐服务 3. 创建外观类4. 在 Controller 中使用外…

安卓(android)实现注册界面【Android移动开发基础案例教程(第2版)黑马程序员】

一、实验目的&#xff08;如果代码有错漏&#xff0c;可查看源码&#xff09; 1.掌握LinearLayout、RelativeLayout、FrameLayout等布局的综合使用。 2.掌握ImageView、TextView、EditText、CheckBox、Button、RadioGroup、RadioButton、ListView、RecyclerView等控件在项目中的…

Prompt提示词完整案例:让chatGPT成为“书单推荐”的高手

大家好&#xff0c;我是老六哥&#xff0c;我正在共享使用AI提高工作效率的技巧。欢迎关注我&#xff0c;共同提高使用AI的技能&#xff0c;让AI成功你的个人助理。 许多人可能会跟老六哥一样&#xff0c;有过这样的体验&#xff1a;当我们遇到一个能力出众或对事物有独到见解的…

智慧园区管理平台实现智能整合提升企业运营模式与管理效率

内容概要 在当今数字化的背景下&#xff0c;智慧园区管理平台正逐渐成为企业提升运营效率和管理模式的重要工具。这个平台汇聚了多种先进技术&#xff0c;旨在通过智能整合各类资源与信息&#xff0c;帮助企业实现全面的管理创新。 智慧园区管理平台不仅仅是一个数据处理工具…

Baklib如何提升企业知识管理效率与市场竞争力的五大对比分析

内容概要 在信息化时代&#xff0c;企业在知识管理方面面临着巨大的挑战与机遇。为了有效应对这些挑战&#xff0c;“Baklib”作为一个知识中台&#xff0c;通过其高度集成的数字化平台&#xff0c;为企业提供全方位的知识管理解决方案。特别是在以下五个领域&#xff0c;它展…

c++:vector

1.使用 1.1构造函数 常见的三种构造方式&#xff1a;空构造&#xff0c;拷贝构造&#xff0c;指定元素构造 1.2iterator begin和end也分为正向和反向。 注意&#xff1a;反向迭代器可以反向遍历是因为在定义rbegin和rend函数的时候把尾地址给到了rbegin&#xff0c;而不是说改…

C++17 搜索器教程:解锁高效搜索新姿势

C17搜索器教程&#xff1a;解锁高效搜索新姿势 C17搜索器简介 在C的发展历程中&#xff0c;C17是一个重要的里程碑&#xff0c;它引入了诸多实用的新特性&#xff0c;搜索器功能便是其中之一。此功能着重对std::search算法进行了强化&#xff0c;使其支持多种搜索策略&#x…

Transformer+vit原理分析

目录 一、Transformer的核心思想 1. 自注意力机制&#xff08;Self-Attention&#xff09; 2. 多头注意力&#xff08;Multi-Head Attention&#xff09; 二、Transformer的架构 1. 整体结构 2. 编码器层&#xff08;Encoder Layer&#xff09; 3. 解码器层&#xff08;Decoder…

C语言自定义数据类型详解(二)——结构体类型(下)

书接上回&#xff0c;前面我们已经给大家介绍了如何去声明和创建一个结构体&#xff0c;如何初始化结构体变量等这些关于结构体的基础知识。下面我们将继续给大家介绍和结构体有关的知识&#xff1a; 今天的主题是&#xff1a;结构体大小的计算并简单了解一下位段的相关知识。…