动机

  • 在软件系统中,经常面临着一系列相关或相互依赖的对象的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。(注:系列会增多,但系列下的功能不会有变化)
  • 如何绕过常规的对象创建方法(new),提供一种”封装机制”来避免客户程序和这种”多系列具体对象创建工作”的紧耦合?

定义

  • 提供一个接口以创建一系列相关或相互依赖的对象,而无需制定他们的具体类。

引出问题

  • 一个程序中读取数据库的内容
  • 读取过程分几个步骤,要用到多个对象,且对象之间有关联性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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();

    }
    };
  • 上述程序只是对sql数据库进行了支持,为了扩展更多类型的支持,这里需要使用抽象接口
  • 此时就可以使用工厂模式进行重构

使用工厂模式重构

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
10
11
12
13
14
15
16
17
18
19
20
21
//内部实现忽略
class IDBConnectionFactory {
public:
IDBConnectionFactory();
~IDBConnectionFactory(){}
virtual IDBConnection* CreateIDBConnection() = 0;
}

class IDBCommandFactory {
public:
IDBCommandFactory();
~IDBCommandFactory(){}
virtual IDBCommand* CreateIDBCommand() = 0;
}

class IDBDataReaderFactory {
public:
IDBDataReaderFactory();
~IDBDataReaderFactory(){}
virtual IDBDataReader* CreateIDBDataReader() = 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//内部实现忽略

//for sql server
class sqlConnectionFactory : public IDBConnectionFactory{
IDBConnection* CreateIDBConnection() override {...}
}
class sqlCommandFactory : public IDBCommandFactory {
IDBCommand* CreateIDBCommand() override {...}
}
class sqlDataReaderFactory : public IDBDataReaderFactory {
IDBDataReader* CreateIDBDataReader() override {...}
}

//for Oracle server
class OracleConnectionFactory : public IDBConnectionFactory{
IDBConnection* CreateIDBConnection() override {...}
}
class OracleCommandFactory : public IDBCommandFactory {
IDBCommand* CreateIDBCommand() override {...}
}
class OracleDataReaderFactory : public IDBDataReaderFactory {
IDBDataReader* CreateIDBDataReader() override {...}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class EmployeeDAO{
//通过EmployeeDAO的构造函数初始化一下三个抽象指针
IDBConnectionFactory * connectFactory;
IDBCommandFactory * commandFactory;
IDBDataReaderFactory * dataReaderFactory;
public:
//读取数据库的操作流程
vector<EmployeeDO> GetEmployees(){
IDBConnection* connection = connectFactory->CreateIDBConnection();
connection->ConnectionString = "...";

IDBCommand* command = commandFactory->CreateIDBCommand();
command->CommandText="...";
command->SetConnection(connection); //注意这里的相关性

IDBDataReader* reader = dataReaderFactory->CreateIDBDataReader();
}
};
  • 代码写到这里似乎已经不错了,抽象接口和具体实现已经完成了隔离。
  • 这里就要再次提到开头强调的一系列相关或相互依赖,在本例中,IDBCommand::SetConnection依赖于IDBConnection对象,具有关联性
  • 具体讲就是这里三个对象应该是同一种数据库,属于高内聚的对象。
    1
    2
    3
    IDBConnectionFactory * 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
    16
    class 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 -> EmployeeDAO
    • AbstactProduct -> [IDBConnection] or [IDBCommand] or [IDataReader]
    • AbstactFactory -> IDBFactory
  • 如果没有应对多系列对象构建的需求变化,则没有必要使用抽象工厂模式,这时候使用简单工厂模式即可
  • 系列对象指的是在某一特定系列下的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖(如上述例子中的sql和oracle)
  • 抽象工厂模式主要在于应对新系列的需求变动,其缺点在于难以应对新对象的需求变动。