“对象创建”模式
通过“对象创建” 模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。
典型模式
•Factory Method
•Abstract Factory
•Prototype
•Builder
动机(Motivation)
在软件系统中,经常面临着创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化。
如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“具体对象创建工作”的紧耦合?
class FileSplitter
{
//...
void split(){
//...
}
};
class MainForm : public Form
{
//...
public:
void Button1_Click(){
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());
FileSplitter* splitter = new FileSplitter(); //应该声明为抽象类,而不是具体的类型
splitter->split();
}
};
在上面代码中,实现了文件分割器。一个类型,要看到未来变化的需求。这是就要做抽象类,即面向接口的编程。一个对象的类型,往往应该声明为抽象类或者接口,而不应该声明成具体的类。如果声明为具体类型,就定死了,不能应对未来的变化。
假设还要支持文本分割TxtSplitter()、图片分割PictureSplitter()等等。抽象出一个基类ISplitter()。
class ISplitter{ // 抽象基类
public:
virtual void split()=0;
virtual ~ISplitter(){}
};
class BinarySplitter : public ISplitter{
};
class TxtSplitter: public ISplitter{
};
class PictureSplitter: public ISplitter{
};
class MainForm : public Form
{
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void Button1_Click(){
//...
ISplitter * splitter= // 使用抽象类
new BinarySplitter();//依赖具体类
splitter->split();
}
};
虽然使用了抽象类型进行变量的声明,但是使用 new 创建对象时仍然依赖具体类。既然不能使用new创建对象,那能不能创建一种方法使它返回一个对象呢?
//抽象类
class ISplitter{
public:
virtual void split()=0;
virtual ~ISplitter(){}
};
//工厂基类
class SplitterFactory{
public:
virtual ISplitter* CreateSplitter()=0;
virtual ~SplitterFactory(){}
};
//具体类
class BinarySplitter : public ISplitter{
};
class TxtSplitter: public ISplitter{
};
class PictureSplitter: public ISplitter{
};
//具体工厂,每一个类都有对应的工厂
class BinarySplitterFactory: public SplitterFactory{
public:
virtual ISplitter* CreateSplitter(){
return new BinarySplitter();
}
};
class TxtSplitterFactory: public SplitterFactory{
public:
virtual ISplitter* CreateSplitter(){
return new TxtSplitter();
}
};
class PictureSplitterFactory: public SplitterFactory{
public:
virtual ISplitter* CreateSplitter(){
return new PictureSplitter();
}
};
class MainForm : public Form
{
SplitterFactory* factory;//工厂
public:
MainForm(SplitterFactory* factory){ // 从外界传递具体的SplitterFactory类型
this->factory=factory;
}
void Button1_Click(){
ISplitter * splitter=
factory->CreateSplitter(); //多态new
splitter->split();
}
};
改良之后,MainForm已经不依赖具体类了。MainForm依赖于抽象类和工厂基类,而不依赖于具体类和具体工厂。
模式定义
定义一个用于创建对象的接口(即工厂基类),让子类(具体工厂)决定实例化哪一个类。Factory Method使得一个类的实例化延迟(目的:解耦 new 和具体类,手段:虚函数)到子类。
要点总结
Factory Method模式用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱。
Factory Method模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
Factory Method模式解决“单个对象”的需求变化。缺点在于要求创建方法/参数相同。
动机(Motivation)
在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。
如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合?
代码示例
数据访问层,要创建一系列的对象。假设访问的是SQLSever的数据库,则要创建链接对象SqlConnection,命令对象SqlCommand,SqlDataReader对象。但数据库有各种各样的选择,可能后面又要使用MySQL。
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()){
}
}
};
为访问多种数据库,创建数据库访问相关的基类。因为上面代码使用new创建对象,使用工厂模式,代码如下。
//数据库访问有关的基类
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* dbCommandFactory;
// 构造函数,传入具体类型
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()){
}
}
};
上面代码涉及多个基类(IDBConnection、IDBCommand、IDataReader),要为每个基类都设计一个对应的工厂基类(IDBConnectionFactory、IDBCommandFactory、IDataReaderFactory)。
上面代码基本解决了问题,但是还有缺陷:在EmployeeDAO中,成员dbConnectionFactory、dbCommandFactory、dbCommandFactory是有关联性的,所以要么都是SQL,要么都是Oracle,不能有的是SQL,有的是Oracle.不能建立了SQL的connection,有创建了Oracle的commend。
//数据库访问有关的基类
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()){
}
}
};
模式定义
提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。
要点总结
如果没有应对“多系列对象构建”的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的工厂完全可以。
“系列对象”指的是在某一特定系列下的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖。
Abstract Factory模式主要在于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。
例如,上面示例中,可以很好支持各种数据库,但如果要加一个新对象的话,就要在抽象工厂基类中添加新的对象,使稳定的内容发生了变化。