7.抽象工厂(Abstract Factory)

ops/2025/2/7 3:59:56/

抽象工厂与工厂方法极其类似,都是绕开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)


http://www.ppmy.cn/ops/156334.html

相关文章

基于Java、SSM、HTML、Vue在线视频教学网课管理系统设计

摘要 随着互联网技术的飞速发展&#xff0c;在线教育市场呈现出蓬勃的发展态势。本论文聚焦于在线视频教学网课管理系统的设计与实现&#xff0c;该系统基于Java语言&#xff0c;运用SSM&#xff08;Spring SpringMVC MyBatis&#xff09;框架构建后端服务&#xff0c;结合H…

Deep Sleep 96小时:一场没有硝烟的科技保卫战

2025年1月28日凌晨3点&#xff0c;当大多数人还沉浸在梦乡时&#xff0c;一场没有硝烟的战争悄然打响。代号“Deep Sleep”的服务器突遭海量数据洪流冲击&#xff0c;警报声响彻机房&#xff0c;一场针对中国关键信息基础设施的网络攻击来势汹汹&#xff01; 面对美国发起的这场…

VSCode中使用EmmyLua插件对Unity的tolua断点调试

一.VSCode中搜索安装EmmyLua插件 二.创建和编辑launch.json文件 初始的launch.json是这样的 手动编辑加上一段内容如下图所示&#xff1a; 三.启动调试模式&#xff0c;并选择附加的进程

梯度提升用于高效的分类与回归

人工智能例子汇总&#xff1a;AI常见的算法和例子-CSDN博客 使用 决策树&#xff08;Decision Tree&#xff09; 实现 梯度提升&#xff08;Gradient Boosting&#xff09; 主要是模拟 GBDT&#xff08;Gradient Boosting Decision Trees&#xff09; 的原理&#xff0c;即&a…

【Elasticsearch】geohex grid聚合

在 Elasticsearch 中&#xff0c;地理边界过滤是一种用于筛选地理数据的技术&#xff0c;它可以根据指定的地理边界形状&#xff08;如矩形、多边形等&#xff09;来过滤符合条件的文档。这种方法在地理空间数据分析中非常有用&#xff0c;尤其是在需要将数据限制在特定地理区域…

C# Monitor类 使用详解

总目录 前言 在 C# 中&#xff0c;Monitor 类是一个用于实现线程同步的重要工具&#xff0c;它提供了一种机制来确保同一时间只有一个线程可以访问特定的代码块或资源&#xff0c;从而避免多线程环境下的数据竞争和不一致问题。下面将对 Monitor 类进行详细介绍。 一、Monitor…

day38|leetcode 322零钱兑换,279.完全平方数,139.单词拆分

322. 零钱兑换 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。 你可以认为每种硬币的数量是…

我的鸿蒙学习之旅:探索万物互联的新宇宙

在科技飞速发展的今天&#xff0c;操作系统领域的创新层出不穷。华为鸿蒙系统的出现&#xff0c;犹如一颗璀璨的新星&#xff0c;照亮了万物互联的未来之路。怀着对新技术的好奇与渴望&#xff0c;我踏上了学习鸿蒙的征程&#xff0c;这段经历充满了挑战与惊喜&#xff0c;也让…