动机
- 在软件系统中,经常面临着一系列相关或相互依赖的对象的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。(注:系列会增多,但系列下的功能不会有变化)
- 如何绕过常规的对象创建方法(new),提供一种”封装机制”来避免客户程序和这种”多系列具体对象创建工作”的紧耦合?
定义
- 提供一个接口以创建一系列相关或相互依赖的对象,而无需制定他们的具体类。
引出问题
- 一个程序中读取数据库的内容
- 读取过程分几个步骤,要用到多个对象,且对象之间有关联性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class EmployeeDAO{
public:
//读取数据库的操作流程
vector<EmployeeDO> GetEmployees(){
SqlConnection* connection = new SqlConnection();
connection->ConnectionString = "...";
SqlCommand* command = new SqlCommand();
command->CommandText="...";
command->SetConnection(connection); //注意这里的相关性
SqlDataReader* reader = command->ExecuteReader();
}
}; - 上述程序只是对sql数据库进行了支持,为了扩展更多类型的支持,这里需要使用抽象接口
- 此时就可以使用工厂模式进行重构
使用工厂模式重构
1 | //内部实现忽略 |
1 | //for Sql server |
1 | //内部实现忽略 |
1 | //内部实现忽略 |
1 | class EmployeeDAO{ |
- 代码写到这里似乎已经不错了,抽象接口和具体实现已经完成了隔离。
- 这里就要再次提到开头强调的一系列相关或相互依赖,在本例中,
IDBCommand::SetConnection依赖于IDBConnection对象,具有关联性 - 具体讲就是这里三个对象应该是同一种数据库,属于高内聚的对象。
1
2
3IDBConnectionFactory * connectFactory;
IDBCommandFactory * commandFactory;
IDBDataReaderFactory * dataReaderFactory; - 如何处理这种情况就引出抽象工厂。
使用抽象工厂重构
- 既然上述三个工厂具有相关性,那么为什么不用一个工厂来完成呢
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//内部实现忽略
class IDBConnection {
public:
IDBConnection();
~IDBConnection(){}
}
class IDBCommand {
public:
IDBCommand();
~IDBCommand(){}
virtual SetConnection() = 0;
}
class IDBDataReader {
public:
IDBDataReader();
~IDBDataReader(){}
}1
2
3
4
5
6
7
8
9//for Sql server
class SqlConnection : public IDBConnection {}
class SqlCommand: public IDBCommand {}
class SqlDataReader : public IDBReader {}
//for Oracle server
class OracleConnection : public IDBConnection {}
class OracleCommand: public IDBCommand {}
class OracleDataReader : public IDBDataReader {}1
2
3
4
5
6
7
8
9//内部实现忽略
class IDBFactory {
public:
IDBFactory();
~IDBFactory(){}
virtual IDBConnection* CreateIDBConnection() = 0;
virtual IDBCommand* CreateIDBCommand() = 0;
virtual IDBDataReader* CreateIDBDataReader() = 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//内部实现忽略
//for sql server
class SqlFactory : public IDBFactory{
IDBConnection* CreateIDBConnection() override {...}
IDBCommand* CreateIDBCommand() override {...}
IDBDataReader* CreateIDBDataReader() override {...}
}
//for Oracle server
class OracleFactory : public IDBFactory{
IDBConnection* CreateIDBConnection() override {...}
IDBCommand* CreateIDBCommand() override {...}
IDBDataReader* CreateIDBDataReader() override {...}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class EmployeeDAO{
//通过EmployeeDAO的构造函数初始化一下三个抽象指针
IDBFactory * factory;
public:
//读取数据库的操作流程
vector<EmployeeDO> GetEmployees(){
IDBConnection* connection = factory->CreateIDBConnection();
connection->ConnectionString = "...";
IDBCommand* command = factory->CreateIDBCommand();
command->CommandText="...";
command->SetConnection(connection); //注意这里的相关性
IDBDataReader* reader = factory->CreateIDBDataReader();
}
}; - 通过将高内聚的对象使用放在同一个工厂内,从而实现高内聚,松耦合。
总结
- 类图
client->EmployeeDAOAbstactProduct-> [IDBConnection] or [IDBCommand] or [IDataReader]AbstactFactory->IDBFactory
- 如果没有应对多系列对象构建的需求变化,则没有必要使用抽象工厂模式,这时候使用简单工厂模式即可
- 系列对象指的是在某一特定系列下的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖(如上述例子中的sql和oracle)
- 抽象工厂模式主要在于应对新系列的需求变动,其缺点在于难以应对新对象的需求变动。