与上篇介绍的Factory Method工厂方法模式一样,Abstract Factory 抽象工厂模式也属于典型的“对象创建模式”模式,解决的问题也极其相似,在理解了Factory Method工厂方法模式的基础上再去理解Abstract Factory 抽象工厂模式就会变得更加容易。
文章目录
- 1. 动机(Motivation)
- 2. 代码演示Factory Method工厂方法模式
- 2.1 常规方法
- 2.2 Factory Method工厂方法
- 2.3 Abstract Factory 抽象工厂模式
- 3. 模式定义
- 4. 结构(Structure)
- 5. 要点总结
- 6. 其他参考
1. 动机(Motivation)
- 在软件系统中,经常面临着
“一系列相互依赖的对象”
的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。 - 如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合?
2. 代码演示Factory Method工厂方法模式
结合代码分析什么是“一系列相互依赖的对象”
2.1 常规方法
假如有一个任务需要写一个访问的是SQL Server的数据访问层,在数据访问层需要创建一系列对象,需要创建数据库的链接SqlConnection* connection
,创建数据库的命令对象SqlCommand* command
,创建数据库的DataReader对象SqlDataReader* reader
,这些都是跟SQL Server相关的。
class EmployeeDAO{
public:
vector<EmployeeDO> GetEmployees(){
SqlConnection* connection =
new SqlConnection();
connection->ConnectionString = "...";
SqlCommand* command =
new SqlCommand();
command->CommandText="...";
command->SetConnection(connection);
SqlDataReader* reader = command->ExecuteReader();
while (reader->Read()){
}
}
};
但是当客户的数据库发生变化,变为Oracle、MySQL、DB2等,对应的类型就需要变化,一旦使用new
就会绑死在SQL Server上,这个类就不适用于多种数据库的变化,假设需要支持多种数据库,大家第一个反应就是需要做到面向接口的编程。
2.2 Factory Method工厂方法
在上面代码的基础进行修改,结合上篇介绍的引入Factory Method工厂方法来代替new
,就得到使用Factory Method工厂方法模式解决问题的代码。
//数据库访问有关的基类接口
class IDBConnection{
};
class IDBConnectionFactory{
public:
virtual IDBConnection* CreateDBConnection()=0;
};
class IDBCommand{
};
class IDBCommandFactory{
public:
virtual IDBCommand* CreateDBCommand()=0;
};
class IDataReader{
};
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()){
}
}
};
上面的实现方法也已经解决了问题,但是暴露了什么问题
IDBConnectionFactory* dbConnectionFactory;
中假如传入的是SqlConnectionFactory
,但是IDBCommandFactory* dbCommandFactory;
中是否可以传入OracleCommandFactory,显然是不可以的。这三个对象必须是同系列同组的,他们之间存在关联性,command和connection等之间是存在关联的。
这里就带来紊乱性,假如未来传入了不同的factory给你,出现传入SqlConnectionFactory、OracleCommandFactory、MySQLDataReaderFactory那就乱套了,无法搭配到一起,这就引出了Abstract Factory 抽象工厂模式。
2.3 Abstract Factory 抽象工厂模式
//数据库访问有关的基类
class IDBConnection{
};
class IDBCommand{
};
class IDataReader{
};
class IDBFactory{
public:
virtual IDBConnection* CreateDBConnection()=0;
virtual IDBCommand* CreateDBCommand()=0;
virtual IDataReader* CreateDataReader()=0;
};
//支持SQL Server
class SqlConnection: public IDBConnection{
};
class SqlCommand: public IDBCommand{
};
class SqlDataReader: public IDataReader{
};
class SqlDBFactory:public IDBFactory{
public:
virtual IDBConnection* CreateDBConnection()=0;
virtual IDBCommand* CreateDBCommand()=0;
virtual IDataReader* CreateDataReader()=0;
};
//支持Oracle
class OracleConnection: public IDBConnection{
};
class OracleCommand: public IDBCommand{
};
class OracleDataReader: public IDataReader{
};
class EmployeeDAO{
IDBFactory* dbFactory;
public:
vector<EmployeeDO> GetEmployees(){
IDBConnection* connection =
dbFactory->CreateDBConnection();
connection->ConnectionString("...");
IDBCommand* command =
dbFactory->CreateDBCommand();
command->CommandText("...");
command->SetConnection(connection); //关联性
IDBDataReader* reader = command->ExecuteReader(); //关联性
while (reader->Read()){
}
}
};
因为这3个factory特别有相关性,那用1个factory就可以了,将这3个类放在一起,实现高内聚松耦合。
大家看到这里就发现使用一个工厂,这就保证了关联性,解决了上面提到问题。从上面的代码可以看出Abstract Factory 其实被称为Family Factory
更为合适,但是GOF/gaofour/4位大师已经定义了这样的名字,我们就不去计较这个名字了。
3. 模式定义
提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。
——《设计模式》GoF
“提供一个接口”的接口就是IDBFactory
,创建了IDBConnection、IDBCommand、IDataReader等
4. 结构(Structure)
上图是《设计模式》GoF中定义的Abstract Factory 抽象工厂模式的设计结构。结合上面的代码看图中最重要的是看其中稳定和变化部分,也就是下图中红框、蓝框和绿框框选的部分。
5. 要点总结
- 如果没有应对“多系列对象构建”的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的工厂完全可以。
- “系列对象”指的是在某一特定系列下的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖。
- Abstract Factory模式主要在于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。
第三点的意思是可以增加SQL、Oracle等系列,但是类内增加新对象就不可以了,因为SqlDBFactory是一个抽象基类,抽象基类要求稳定,这就是该模式的缺点,模式中稳定的部分就是缺点
。
当所有地方都变化就没有模式可以解决,当所有地方都稳定
Abstract Factory 抽象工厂模式其实跟Factory Method工厂方法模式,很接近,也可以说Factory Method工厂方法模式是Abstract Factory 抽象工厂模式的特例。
当以下代码创建三个对象
class IDBFactory{
public:
virtual IDBConnection* CreateDBConnection()=0;
virtual IDBCommand* CreateDBCommand()=0;
virtual IDataReader* CreateDataReader()=0;
};
变为创建一个对象,就是Factory Method工厂方法模式
class IDBFactory{
public:
virtual IDBConnection* CreateDBConnection()=0;
};
有些地方直接将Abstract Factory 抽象工厂模式和Factory Method工厂方法模式直接称作工厂模式
。
6. 其他参考
C++设计模式——抽象工厂模式