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

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)


http://www.kler.cn/a/525673.html

相关文章:

  • sem_wait的概念和使用案列
  • 【重生之我在学习C语言指针详解】
  • 基于Springboot的智能学习平台系统【附源码】
  • 【Matlab高端绘图SCI绘图模板】第006期 对比绘柱状图 (只需替换数据)
  • IME关于输入法横屏全屏显示问题-Android14
  • 渗透测试之WAF规则触发绕过规则之规则库绕过方式
  • P1002 [NOIP2002 普及组] 过河卒
  • Leetcode 131 分割回文串(纯DFS)
  • EtherCAT主站IGH-- 23 -- IGH之fsm_slave.h/c文件解析
  • 在Ubuntu下编译VLC
  • 【AI非常道】二零二五年一月(二),AI非常道
  • 正态分布与柯西分布的线性组合与副本随机变量同分布
  • Spring Boot + Facade Pattern : 通过统一接口简化多模块业务
  • 【C语言】函数递归
  • 【LeetCode: 958. 二叉树的完全性检验 + bfs + 二叉树】
  • 【自学笔记】MySQL的重点知识点-持续更新
  • 《LLM大语言模型+RAG实战+Langchain+ChatGLM-4+Transformer》
  • 【C++动态规划 离散化】1626. 无矛盾的最佳球队|2027
  • 受击反馈HitReact、死亡效果Death Dissolve、Floating伤害值Text(末尾附 客户端RPC )
  • Git 版本控制:基础介绍与常用操作
  • 当代搜索引擎技术介绍性能优化
  • MySQL UNION 操作详解
  • 数据结构初阶之堆的介绍与堆的实现
  • 如何安装 CUDA Toolkits
  • 从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(基础图形库实现)
  • 开源2+1链动模式AI智能名片S2B2C商城小程序:利用用户争强好胜心理促进分享行为的策略研究