【前言】在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。

引言

什么叫“一系列相互依赖的对象”?

假设我们目前使用的是SQL Server数据库,要实现一个对SQL Server数据库的访问层。那么,紧接着定义了数据库连接对象数据库命令对象数据库的DataReader对象,而这些对象都是与SQL Server相关。

  1. //数据访问层
  2. class EmployeeDAO{
  3. public:
  4. //假设此为SQL Server的数据库
  5. //创建一系列的对象
  6. vector<EmployeeDO> GetEmployees(){
  7. //数据库的连接
  8. SqlConnection* connection = new SqlConnection();
  9. connection->ConnectionString = "...";
  10. //数据库的命令对象
  11. SqlCommand* command = new SqlCommand();
  12. command->CommandText="...";
  13. command->SetConnection(connection);
  14. //数据库的DataReader对象
  15. SqlDataReader* reader = command->ExecuteReader();
  16. while (reader->Read()){
  17. }
  18. }
  19. };

【变化】但是如果你的客户需要让你更改数据库,上面所创建的数据类型就可能要发生变化。
所以,使用new是不合适的。因为new写死了数据类型,如果变化一个数据库,这个类型就不适用于其他数据库。

面向接口编程

定义一些虚基类,用面向接口编程的思想来应对这种变化。

  1. /////数据库访问有关的基类
  2. class IDBConnection{
  3. };
  4. class IDBCommand{
  5. };
  6. class IDataReader{
  7. };
  8. /////支持SQL Server的数据结构
  9. class SqlConnection: public IDBConnection{
  10. };
  11. class SqlCommand: public IDBCommand{
  12. };
  13. class SqlDataReader: public IDataReader{
  14. };
  15. /////支持Oracle
  16. class OracleConnection: public IDBConnection{
  17. };
  18. class OracleCommand: public IDBCommand{
  19. };
  20. class OracleDataReader: public IDataReader{
  21. };
  22. /////数据访问层
  23. class EmployeeDAO{
  24. IDBConnectionFactory* dbConnectionFactory;
  25. IDBCommandFactory* dbCommandFactory;
  26. IDataReaderFactory* dataReaderFactory;
  27. public:
  28. vector<EmployeeDO> GetEmployees(){
  29. //换位抽象基类,以应对这些变化
  30. IDBConnection* connection = new SqlConnection(); //?
  31. connection->ConnectionString("...");
  32. IDBCommand* command = new SqlCommand(); //?
  33. command->CommandText("...");
  34. command->SetConnection(connection);
  35. IDBDataReader* reader = command->ExecuteReader();
  36. while (reader->Read()){
  37. }
  38. }
  39. };

虽然,对象的类型换成了抽象基类。但是,第33行、36行怎么办?new后面是一个具体的类型。此时,就想到了工厂设计模式,定义一些工厂来解决这些细节依赖,用类的方法来代替new。

  1. /////数据库访问有关的基类,和对应的工厂
  2. class IDBConnection{
  3. };
  4. class IDBConnectionFactory{
  5. public:
  6. virtual IDBConnection* CreateDBConnection()=0;
  7. };
  8. class IDBCommand{
  9. };
  10. class IDBCommandFactory{
  11. public:
  12. virtual IDBCommand* CreateDBCommand()=0;
  13. };
  14. class IDataReader{
  15. };
  16. class IDataReaderFactory{
  17. public:
  18. virtual IDataReader* CreateDataReader()=0;
  19. };
  20. /////支持SQL Server
  21. class SqlConnection: public IDBConnection{
  22. };
  23. class SqlConnectionFactory:public IDBConnectionFactory{
  24. };
  25. class SqlCommand: public IDBCommand{
  26. };
  27. class SqlCommandFactory:public IDBCommandFactory{
  28. };
  29. class SqlDataReader: public IDataReader{
  30. };
  31. class SqlDataReaderFactory:public IDataReaderFactory{
  32. };
  33. /////支持Oracle
  34. class OracleConnection: public IDBConnection{
  35. };
  36. class OracleConnectionFactory:public IDBConnectionFactory{
  37. };
  38. class OracleCommand: public IDBCommand{
  39. };
  40. class OracleCommandFactory: public IDBCommandFactory{
  41. };
  42. class OracleDataReader: public IDataReader{
  43. };
  44. class OracleDataReaderFactory: public IDataReaderFactory{
  45. };
  46. /////数据访问层
  47. class EmployeeDAO{
  48. //这三个变量就要在EmployeeDAO构造器里传进来具体的工厂
  49. IDBConnectionFactory* dbConnectionFactory;
  50. IDBCommandFactory* dbCommandFactory;
  51. IDataReaderFactory* dataReaderFactory;
  52. public:
  53. vector<EmployeeDO> GetEmployees(){
  54. IDBConnection* connection = dbConnectionFactory->CreateDBConnection();
  55. connection->ConnectionString("...");
  56. IDBCommand* command = dbCommandFactory->CreateDBCommand();
  57. command->CommandText("...");
  58. command->SetConnection(connection); //关联性
  59. IDBDataReader* reader = command->ExecuteReader(); //关联性
  60. while (reader->Read()){
  61. }
  62. }
  63. };

关联性(抽象工厂)

【关联性】IDBConnection、IDBCommand、IDBDataReader之间具有关联性

  1. 如果用的是SQL Server,三者都要是支持SQL Server的类型
  2. 并且SQL Server的IDBCommand中要设置SQL Server的IDBConnection,IDBDataReader也是一样
  3. 这就是三个类型之间的关联性,它们并不是独立的。三者是要属于同一个族,而这个族取决于数据库

【问题】如果出现一个人,他传入的IDBConnectionFactory是SQL Server的DBConnectionFactoryIDBCommandFactory是MySQL的DBCommandFactory,会有什么问题?

  1. 在两者设置关联性的时候,就会报错(command->SetConnection(connection);

【解决思路】那把IDBConnectionFactoryIDBCommandFactoryIDataReaderFactory的三个方法放在一起,放在一个工厂里,可不可以有所改善?

  1. //数据库访问有关的基类
  2. class IDBConnection{
  3. };
  4. class IDBCommand{
  5. };
  6. class IDataReader{
  7. };
  8. //抽象工厂,管理着有关联性的产品创建
  9. class IDBFactory{
  10. public:
  11. virtual IDBConnection* CreateDBConnection()=0;
  12. virtual IDBCommand* CreateDBCommand()=0;
  13. virtual IDataReader* CreateDataReader()=0;
  14. };
  15. //支持SQL Server
  16. class SqlConnection: public IDBConnection{
  17. };
  18. class SqlCommand: public IDBCommand{
  19. };
  20. class SqlDataReader: public IDataReader{
  21. };
  22. class SqlDBFactory:public IDBFactory{
  23. public:
  24. virtual IDBConnection* CreateDBConnection()=0;
  25. virtual IDBCommand* CreateDBCommand()=0;
  26. virtual IDataReader* CreateDataReader()=0;
  27. };
  28. //支持Oracle
  29. class OracleConnection: public IDBConnection{
  30. };
  31. class OracleCommand: public IDBCommand{
  32. };
  33. class OracleDataReader: public IDataReader{
  34. };
  35. class OracleDBFactory:public IDBFactory{
  36. public:
  37. virtual IDBConnection* CreateDBConnection()=0;
  38. virtual IDBCommand* CreateDBCommand()=0;
  39. virtual IDataReader* CreateDataReader()=0;
  40. };
  41. class EmployeeDAO{
  42. IDBFactory* dbFactory; //抽象工厂
  43. public:
  44. vector<EmployeeDO> GetEmployees(){
  45. IDBConnection* connection = dbFactory->CreateDBConnection();
  46. connection->ConnectionString("...");
  47. IDBCommand* command = dbFactory->CreateDBCommand();
  48. command->CommandText("...");
  49. command->SetConnection(connection); //关联性
  50. IDBDataReader* reader = command->ExecuteReader(); //关联性
  51. while (reader->Read()){
  52. }
  53. }
  54. };

高内聚、松耦合。如果有相关性的就能放在一个工厂内部。
如此,就保证了三者的关联性,保证了它们是属于一个族的。

其实,这个名字取的不好,“抽象工厂”。叫FamilyFactory可能会贴切一点。

动机

在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。

  1. SQLServer是一个系列;MySQL是一个系列,Oracle是一个系列
  2. 如果应对这些变化呢?就是绕开new

如果应对这种变化?如果绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合。

模式定义

提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。——《设计模式》GoF

结构

image.png

要点总结

  1. 如果没有应用“多系列对象构建”的需求变化,则没有必要使用AbstractFactory模式,这时候使用简单的工厂完全可以。
  2. “系列对象”指的是在某一特定系列下的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖
  3. AbstractFactory模式主要在于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。

注释:

  1. 其实,这个名字取的不好,“抽象工厂”。叫FamilyFactory可能会贴切一点。
  2. 任务模式都会有缺点,我们所说的一个模式的稳定部分,就是他的缺点。如果你假设稳定部分将来会变化,那个模式就不使用了。可能会有其他模式可以解决这个变化。
  3. 如果你极端的假设任何一个地方都有变化,那么没有任何一个模式或几个模式合起来,能够解决你的问题。所以说不能够推到一个极端。极端就是所有内容都会变化。
  4. 如果你没有那么多变化,那就没有必要使用模式,直接写就完事了。设计模式是解决稳定中有变化的场景。
  5. 软件中很难走向两个极端:所有地方都有变化;所有地方都是稳定的。所有地方都变化,没有设计模式能够解决这种问题。所有地方都稳定,就没有必要用模式了。