C++设计模式--Factory Method工厂方法和抽象工厂方法

“对象创建”模式
通过“对象创建” 模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。
典型模式
•Factory Method
•Abstract Factory
•Prototype
•Builder

1. Factory Method工厂方法

动机(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 和具体类,手段:虚函数)到子类。

C++设计模式--Factory Method工厂方法和抽象工厂方法_第1张图片
要点总结
Factory Method模式用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱。
Factory Method模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
Factory Method模式解决“单个对象”的需求变化。缺点在于要求创建方法/参数相同

2. Abstract Factory 抽象工厂

动机(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()){

        }

    }
};

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

C++设计模式--Factory Method工厂方法和抽象工厂方法_第2张图片
要点总结
如果没有应对“多系列对象构建”的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的工厂完全可以。
“系列对象”指的是在某一特定系列下的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖。
Abstract Factory模式主要在于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。
例如,上面示例中,可以很好支持各种数据库,但如果要加一个新对象的话,就要在抽象工厂基类中添加新的对象,使稳定的内容发生了变化。

你可能感兴趣的:(笔记,C++,设计模式)