7.抽象工厂(Abstract Factory)

news/2025/1/31 11:42:41 标签: 抽象工厂模式

抽象工厂与工厂方法极其类似,都是绕开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.niftyadmin.cn/n/5838623.html

相关文章

车载软件 --- 大一新生入门汽车零部件嵌入式开发

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 简单&#xff0c;单纯&#xff0c;喜欢独处&#xff0c;独来独往&#xff0c;不易合同频过着接地气的生活…

Vue.js组件开发-实现全屏背景图片滑动切换特效

使用 Vue 实现全屏背景图片滑动切换特效的详细步骤、代码、注释和使用说明。 步骤 创建 Vue 项目&#xff1a;使用 Vue CLI 创建一个新的 Vue 项目。准备图片资源&#xff1a;准备好要用于背景切换的图片&#xff0c;并将它们放在项目的合适目录下。编写 HTML 结构&#xff1…

JAVA 接口、抽象类的关系和用处 详细解析

接口 - Java教程 - 廖雪峰的官方网站 一个 抽象类 如果实现了一个接口&#xff0c;可以只选择实现接口中的 部分方法&#xff08;所有的方法都要有&#xff0c;可以一部分已经写具体&#xff0c;另一部分继续保留抽象&#xff09;&#xff0c;原因在于&#xff1a; 抽象类本身…

Kafka常见问题之 org.apache.kafka.common.errors.RecordTooLargeException

文章目录 Kafka常见问题之 org.apache.kafka.common.errors.RecordTooLargeException: The message is 1,048,576 bytes when serialized which is larger than the maximum request size.1. 错误解析2. 错误原因3. 错误复现案例3.1 生产者发送超大消息 4. 解决方案4.1 方法 1&…

Linux 常用命令——系统设置篇(保姆级说明)

系统设置类 显示当前运行的进程&#xff08;ps&#xff09; ps [options] [--help]# 查找指定进程格式&#xff1a; ps -ef | grep 进程关键字# 显示进程信息 ps -A 参数&#xff1a; -A 列出所有的进程 -w 显示加宽可以显示较多的资讯 -au 显示较详细的资讯 -aux 显示所有包…

DeepSeek 云端部署,释放无限 AI 潜力!

1.简介 目前&#xff0c;OpenAI、Anthropic、Google 等公司的大型语言模型&#xff08;LLM&#xff09;已广泛应用于商业和私人领域。自 ChatGPT 推出以来&#xff0c;与 AI 的对话变得司空见惯&#xff0c;对我而言没有 LLM 几乎无法工作。 国产模型「DeepSeek-R1」的性能与…

GESP2023年12月认证C++六级( 第三部分编程题(1)闯关游戏)

参考程序代码&#xff1a; #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <string> #include <map> #include <iostream> #include <cmath> using namespace std;const int N 10…

LeetCode题练习与总结:分数加减运算--592

一、题目描述 给定一个表示分数加减运算的字符串 expression &#xff0c;你需要返回一个字符串形式的计算结果。 这个结果应该是不可约分的分数&#xff0c;即 最简分数。 如果最终结果是一个整数&#xff0c;例如 2&#xff0c;你需要将它转换成分数形式&#xff0c;其分母…