经过前面模式的学习,我们若未使用抽象设计模式,通常想到的都是使用接口来处理 “多数据库读写” 的问题,然后会进一步的发现使用工厂模式来隔离类对象的使用者和具体类型,如下代码示例。
/**
* 使用普通的工厂模式
*/
//数据库访问有关的基类
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;
};
//支持Mysql
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 OracleConnectionFactory : public IDBConnectionFactory{};
class OracleCommand : public IDBCommand{};
class OracleConnectionFactory : public IDBCommandFactory{};
class OracleDataReader : public IDataReader{};
class OracleConnectionFactory : public IDataReaderFactory{};
class EmployeeDAO{
IDBConnectionFactory* dbConnectionFactory;
IDBCommandFactory* dbCommandFactory;
IDataReaderFactory* dbReaderFactory;
public:
/** 不用接口和工厂模式时会发现这样写依赖了具体类
vector GetEmployees(){
SqlConnection* connection = new SqlConnection();
connection->ConnectionString("...");
SqlCommand* command = new SqlCommand();
command->CommandText("...");
}
*/
vector<EmployeeDD> GetEmployees(){
IDBConnection* connection = dbConnectionFactory->createDBConnection();
connection->ConnectionString("...");
IDBCommand* command = dbCommandFactory->createDBCommand();
command->CommandText("...");
command->SetConnection(connection); //关联性
IDataReader* reader = command->ExecuteReader(); //关联性
while (reader->Read()){
}
}
};
采用普通的工厂模式存在一些问题:如IDBConnectionFactory、IDBCommandFactory、IDataReaderFactory都必须基于同一种数据库,不可能有Oracle的语句去对mysql数据库执行,所以如果给他们传入的不是同一种数据库就会出问题,所以这就体现了connection、create等方法的关联性。而采用了抽象工厂模式,可以将这些关联性放在一起,结构图如下。AbstractFactory相当于我们的IDBFactory;AbstractFactoryA相当于我们的IDBConnection,AbstractFactoryB相当于我们的IDBCommand;ConceteFactory1相当于我们的支持Mysql的SqlDBFactory,ConceteFactory2相当于我们支持Oracle的OracleDBFacotry;ProductA1相当于SqlConnection,ProductA2相当于OracleConnection;ProductB1相当于SqlCommand,ProductB2相当于OracleCommand。
//数据库访问有关的基类
class IDBConnection{};
class IDBCommand{};
class IDataReader{};
class IDBFactory{ //把三个工厂合成一个工厂了
public:
virtual IDBConnection* createDBConnection() = 0;
virtual IDBCommand* createDBCommand() = 0;
virtual IDataReader* createDataReader() = 0;
};
//支持Mysql
class SqlConnection : public IDBConnection{};
class SqlCommand : public IDBCommand{};
class SqlDataReader : public IDataReader{};
class SqlDBFactory : public IDBFactory{
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 OracleDBFactory : public IDBFactory{
virtual IDBConnection* createDBConnection() = 0;
virtual IDBCommand* createDBCommand() = 0;
virtual IDataReader* createDataReader() = 0;
};
class EmployeeDAO{
IDBFactory* dbFactory; //得到了关联性的保证,同一种数据库
public:
vector<EmployeeDD> GetEmployees(){
IDBConnection* connection = dbFactory->createDBConnection();
connection->ConnectionString("...");
IDBCommand* command = dbFactory->createDBCommand();
command->CommandText("...");
command->SetConnection(connection); //关联性
IDataReader* reader = command->ExecuteReader(); //关联性
while (reader->Read()){
}
}
};
(1)模式动机
在软件系统中,经常面临着 “一系列相互依赖的对象” 的创建工作。同时,由于需求的变化,往往存在更多系列对象的创建工作。
如何应对这种系列变化?如何绕过常规的对象创建方法(new),提供一种封装机制来避免客户程序和这种 “多系列具体对象创建工作” 的紧耦合?
(2)模式定义
提供一个接口,让该接口负责创建一系列 “相关或者相互依赖的对象”,无需指定它们具体的类。
(3)要点总结
a). 如果没有应对 “多系列对象构建” 的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的工厂完全可以。
b). “系列对象” 指的是在某一特定系列下的对象之间有相互依赖、或作用的关系,不同系列的对象之间不能相互依赖。
c). Abstract Factory模式主要在于应对 “新系列” 的需求变动。其缺点在于难以应对 “新对象” 的需求变动。
在第一点中已展示出相应的代码,这里不做过多的赘述。我们应该要去好好把握稳定与变化间的关系,软件设计过程中很难走向极端(任何都变化 / 任何都稳定),所以我们可以使用设计模式去解决他们之间的关系。在该模式的设计中,提供接口去处理系列对象是重要的手段。