以下内容均来自GeekBand极客班C++ 设计模式课程(李建忠老师主讲)
Abstract Factory
“对象创建”模型
通过“对象创建”模式绕开new,避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。
典型模式
Factory Method
Abstract Factory
Prototype
Builder
动机(Motivation)
和工厂方法非常类似,都是解决同一类问题
在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。
《设计模式》GOF的定义:
提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。
AbstractFactory代表:IDBFactory
ConcreteFactory1 & ConcreteFactory2 代表:SqlFactory & OraclFactory
AbstractProductA代表:IDBConnection
AbstractProductB代表:IDBCommand
AbstractProductC代表:IDBDataReader
示例
假设有这么一个类,一个数据访问层,现在假设我们访问的是Sql数据库,代码示例如下:
class EmployeeDAO{
public:
vector<EmployeeDAO> GetEmployees(){
SqlConnection * connection =
new SqlConnection();
connection->ConnectionString("...");
SqlCommand * command =
new SqlCommand();
command->SetConnection(connection);//二者之间存在联系
command->CommandText("...");
SqlDataReader * reader = command->ExecuteReader();
while(reader->Read()){
}
}
};
但是如果在可见的未来,我们要访问其他类的数据库,显然GetEmployees函数就依赖了太多具体的内容。
我们先丰满一下示例的其他部分:
//数据库访问有关的基类
class IDBConnection{
};
class IDBCommand{
};
class IDBDataReader{
};
支持SQL Server
//支持SQL Server
class SqlConnection : public IDBConnection{
};
class SqlCommand : public IDBCommand{
};
class SqlDataReader : public IDBDataReader{
};
支持 Oracle
//支持 Oracle
class OracleConnection : public IDBConnection{
};
class OracleCommand : public IDBCommand{
};
class OracleDataReader : public IDBDataReader{
};
我们已经有了抽象类,就可以对EmployeeDAO进行重构了
class EmployeeDAO{
public:
vector<EmployeeDAO> GetEmployees(){
IDBConnection * connection =
new SqlConnection();
connection->ConnectionString("...");
IDBCommand * command =
new SqlCommand();
command->CommandText("...");
IDBDataReader * reader = command->ExecuteReader();
while(reader->Read()){
}
}
};
但是我们发现还是不够的,new还是有问题的,需要将new也去掉才行
如果依旧使用工厂模式:
class IDBConnection{
};
class IDBConnectionFactory{
public:
virtual IDBConnection * CreateDBConnection() = 0;
virtual ~IDBConnectionFactory(){}
};
class IDBCommand{
};
class IDBCommandFactory{
public:
virtual IDBCommand * CreateDBConnection() = 0;
virtual ~IDBCommandFactory(){}
};
class IDBDataReader{
};
class IDBDataReaderFactory{
public:
virtual IDBDataReader * CreateDBConnection() = 0;
virtual ~IDBDataReaderFactory(){}
};
以上是抽象类,下面是具体实现类
//支持SQL Server
class SqlConnection : public IDBConnection{
};
class SqlConnectionFactory : public IDBConnectionFactory{
};
class SqlCommand : public IDBCommand{
};
class SqlCommandFactory : public IDBCommandFactory{
};
class SqlDataReader : public IDBDataReader{
};
class SqlDataReaderFactory : public IDBDataReaderFactory{
};
之后我们对Employee进行修改:
class EmployeeDAO{
IDBConnectionFactory * _ConnectionFactory;
IDBCommandFactory * _CommandFactory;
IDBDataReaderFactory * _DataReaderFactory;
public:
vector<EmployeeDAO> GetEmployees(){
IDBConnection * connection = _ConnectionFactory->CreateDBConnection();
connection->ConnectionString = "...";
IDBCommand * command = _CommandFactory->CreateDBCommand();
command->SetConnection(connection);//二者之间存在联系
command->CommandText = "...";
IDBDataReader * reader = _DataReaderFactory->CreateDBDataReader();
while(reader->Read()){
}
}
};
这个问题目前来看是解决了
但是因为IDBConnection和IDBCommand之间存在联系,不能 IDBConnection指向的是SQL Server但是IDBCommand指向的是Oracle
IDBConnectionFactory * _ConnectionFactory;
IDBCommandFactory * _CommandFactory;
IDBDataReaderFactory * _DataReaderFactory;
三个对象必须是同一个系列的。
我们需要对其进行整合,将三个工厂放在一起
//数据库访问有关的基类
class IDBConnection{
};
class IDBCommand{
};
class IDBDataReader{
};
class IDBFactory{
public:
virtual IDBConnection * CreateDBConnection() = 0;
virtual IDBCommand * CreateDBCommand() = 0;
virtual IDBDataReader * CreateDBDataReader() = 0;
virtual ~IDBFactory(){}
};
那么具体的实现,以SQL Server为例:
//支持SQL Server
class SqlConnection : public IDBConnection{
};
class SqlCommand : public IDBCommand{
};
class SqlDataReader : public IDBDataReader{
};
class SqlFactory : public IDBFactory{
public:
virtual IDBConnection * CreateDBConnection(){
return new SqlConnection();
}
virtual IDBCommand * CreateDBCommand(){
return new SqlCommand();
}
virtual IDBDataReader * CreateDBDataReader(){
return new SqlDataReader();
}
virtual ~SqlFactory(){}
};
此处也体现了高聚合的编程思想:相同的内容,将其放在一起
class EmployeeDAO{
IDBFactory * _Factory;
public:
vector<EmployeeDAO> GetEmployees(){
IDBConnection * connection = _Factory->CreateDBConnection();
connection->ConnectionString = "...";
IDBCommand * command = _Factory->CreateDBCommand();
command->SetConnection(connection);//二者之间存在联系
command->CommandText = "...";
IDBDataReader * reader = _Factory->CreateDBDataReader();
while(reader->Read()){
}
}
};
要点总结
如果没有应对“多系类对象构建”的需求变化,则没有必要使用Avstract Factory模式,这时候使用简单的工厂完全可以。
“系类对象”指的是在某一特定系类下的对象之间有相互依赖,或作用的关系。不同系统的对象之间不能相互依赖。
Abstract Factory模式主要在于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。