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