天天看點

C++設計模式--Prototype 原型模式 和 Builder 建構器

“對象建立”模式

通過“對象建立” 模式繞開new,來避免對象建立(new)過程中所導緻的緊耦合(依賴具體類),進而支援對象建立的穩定。它是接口抽象之後的第一步工作。

典型模式

•Factory Method

•Abstract Factory

•Prototype

•Builder

1. Prototype 原型模式

動機(Motivation)

  1. 在軟體系統中,經常面臨着“某些結構複雜的對象"的建立工作;由于需求的變化,這些對象經常面臨着劇烈的變化,但是它們卻擁有比較穩定一緻的接口。
  2. 如何應對這種變化?如何向“客戶程式(使用這些對象的程式) "隔離出“這些易變對象”,進而使得“依賴這些易變對象的客戶程式"不随着需求改變而改變?

代碼示例

首先看一下工廠模式的代碼

//抽象類
class ISplitter{
public:
    virtual void split()=0;
    virtual ~ISplitter(){}
};

//工廠基類
class SplitterFactory{
public:
    virtual ISplitter* CreateSplitter()=0;
    virtual ~SplitterFactory(){}
};
           

将兩個類合并,将CreateSplitter函數重命名為Clone函數,得到代碼如下

//抽象類
class ISplitter{
public:
    virtual void split()=0;
    virtual ISplitter* clone()=0; //通過克隆自己來建立對象
    
    virtual ~ISplitter(){}
};
           
//具體類
class BinarySplitter : public ISplitter{
public:
    virtual ISplitter* clone(){
        return new BinarySplitter(*this); // 通過拷貝構造函數建立對象
    }
};

class TxtSplitter: public ISplitter{
public:
    virtual ISplitter* clone(){
        return new TxtSplitter(*this);
    }
};

class PictureSplitter: public ISplitter{
public:
    virtual ISplitter* clone(){
        return new PictureSplitter(*this);
    }
};

           
class MainForm : public Form
{
    ISplitter*  prototype;//原型對象

public:
    
    MainForm(ISplitter*  prototype){
        this->prototype=prototype;
    }
    
	void Button1_Click(){

		ISplitter * splitter=
            prototype->clone(); //克隆原型得到一個新對象
        
        splitter->split();
        
	}
};
           

模式定義

使用原型執行個體指定建立對象的種類,然後通過拷貝這些原型來建立新的對象。

C++設計模式--Prototype 原型模式 和 Builder 建構器

什麼時候使用

對于結構複雜的對象,有一個對象已經達到比較好的狀态,将其作為原型,可以克隆出許多。

要點總結

  1. Prototype模式同樣用于隔離類對象的使用者和具體類型(易變類)之間的耦合關系,它同樣要求這些“易變類”擁有“穩定的接口"。
  2. Prototype模式對于“如何建立易變類的實體對象”采用"原型克隆"的方法來做,它使得我們可以非常靈活地動态建立“擁有某些穩定接口"的新對象——所需工作僅僅是注冊一個新類的對象(即原型)然後在任何需要的地方Clone.
  3. Prototype模式中的Clone方法可以利用某些架構中的序列化來實作深拷貝。

網友對原型的介紹 https://www.cnblogs.com/iplus/archive/2012/05/02/4490233.html

2. Builder 建構器

動機(Motivation)

在軟體系統中,有時候面臨着“一個複雜對象”的建立工作,其通常由各個部分的子對象用一定的算法構成;由于需求的變化,這個複雜對象的各個部分經常面臨着劇烈的變化,但是将它們組合在一起的算法卻相對穩定。

如何應對這種變化?如何提供一種“封裝機制”來隔離出“複雜對象的各個部分”的變化,進而保持系統中的“穩定建構算法”不随着需求改變而改變?

代碼示例

遊戲裡面建房子,可以建茅草屋、磚瓦房等等。但有幾個流程是固定的,例如打地基、建牆壁、建房頂等等。不同房子的牆壁、窗子等等可能是不一樣的。

class House{
public:
    // 建房子的流程,穩定的(很像模闆方法)
	void Init(){ // 初始化
		this->BuildPart1();
		for (int i = 0; i < 4; i++){
			this->BuildPart2();
		}
		bool flag = this->BuildPart3();
		if (flag){
			this->BuildPart4();
		}
		this->BuildPart5();
	}
    
    virtual ~House(){}
	
protected:
	// 建房子的具體步驟,可能是變化的,是以定為虛函數
    virtual void BuildPart1()=0;
    virtual void BuildPart2()=0;
    virtual void BuildPart3()=0;
    virtual void BuildPart4()=0;
    virtual void BuildPart5()=0;
};

class StoneHouse: public House{
	//...

protected:	
	//重寫虛函數
    virtual void BuildPart1(){

	}
    virtual void BuildPart2(){

	}
    virtual void BuildPart3(){

	}
    virtual void BuildPart4(){

	}
    virtual void BuildPart5(){

	}
};

int main(){
	House* pHouse = new StoneHouse();// 建構對象
	pHouse->Init();
}
           

在C++中,如果在構造函數裡面調用虛函數,是靜态綁定,是以不能将Init函數中的内容變為構造函數。

在StoneHouse等子類中,對虛函數進行重寫,當時構造過程Init是不變的。到這裡已經基本完成了Build模式。但仍有優化空間:

将對象的表示和建構進行拆分。

// 将House和Housebuilder分離
class House{ //對象的表示
    //....
};

class HouseBuilder { // 對象的建構
public:
    House* GetResult(){
        return pHouse;
    }
    virtual ~HouseBuilder(){}
protected:    
    House* pHouse;
	virtual void BuildPart1()=0;
    virtual void BuildPart2()=0;
    virtual void BuildPart3()=0;
    virtual void BuildPart4()=0;
    virtual void BuildPart5()=0;
	
};

class StoneHouse: public House{
    
};

class StoneHouseBuilder: public HouseBuilder{
protected:
    
    virtual void BuildPart1(){
        //pHouse->Part1 = ...;
    }
    virtual void BuildPart2(){
        
    }
    virtual void BuildPart3(){
        
    }
    virtual void BuildPart4(){
        
    }
    virtual void BuildPart5(){
        
    }
    
};


class HouseDirector{
    
public:
    HouseBuilder* pHouseBuilder;
    
    HouseDirector(HouseBuilder* pHouseBuilder){ // 構造函數,傳入具體類型
        this->pHouseBuilder=pHouseBuilder;
    }
    
    House* Construct() { // 穩定的
    
		pHouseBuilder->BuildPart1();
		for (int i = 0; i < 4; i++){
			pHouseBuilder->BuildPart2();
		}
		bool flag = pHouseBuilder->BuildPart3();
		if (flag){
			pHouseBuilder->BuildPart4();
		}
		pHouseBuilder->BuildPart5();
              
        return pHouseBuilder->GetResult();
    }
};

           

模式定義

将一個複雜對象的建構與其表示相分離,使得同樣的建構過程(穩定)可以建立不同的表示(變化)。

C++設計模式--Prototype 原型模式 和 Builder 建構器

Director相當于HouseDirector,是穩定的,Builder相當于HouseBuilder,也是穩定的,ConcreteBuilder相當于StoneHouseBuilder,是變化的。如果類不是很複雜,Builder和Director可以合在一起,不用拆分。

要點總結

Builder 模式主要用于“分步驟建構一個複雜的對象”。在這其中“分步驟”是一個穩定的算法,而複雜對象的各個部分則經常變化。

變化點在哪裡,封裝哪裡—— Builder模式主要在于應對“複雜對象各個部分”的頻繁需求變動。其缺點在于難以應對“分步驟建構算法”的需求變動。

在Builder模式中,要注意不同語言中構造器内調用虛函數的差别(C++ vs. C#) 。