設計模式--建造者模式Builder(建立型)

benbenxiongyuan發表於2014-04-16

1. 概述

       在軟體開發的過程中,當遇到一個“複雜的物件”的建立工作,該物件由一定各個部分的子物件用一定的演算法構成,由於需求的變化,複雜物件的各個部分經常面臨劇烈的變化,但將它們組合在一起的演算法相對穩定。

       例子1:買肯德基

       典型的兒童餐包括一個主食,一個輔食,一杯飲料和一個玩具(例如漢堡、炸雞、可樂和玩具車)。這些在不同的兒童餐中可以是不同的,但是組合成兒童餐的過程是相同的。

       
       客戶端:顧客想去買一套套餐(這裡麵包括漢堡,可樂,薯條),可以有1號和2號兩種套餐供顧客選擇。
       指導者角色:收銀員。知道顧客想要買什麼樣的套餐,並告訴餐館員工去準備套餐。
       建造者角色:餐館員工。按照收銀員的要求去準備具體的套餐,分別放入漢堡,可樂,薯條等。
       產品角色
最後的套餐,所有的東西放在同一個盤子裡面。

        例子2:計算工資:工資的計算一般是:底薪+獎金-稅。但底薪分為一級8000、二級6000、三級4000三個等級。根據崗位不同獎金的發放也不一樣,管理及日常事務處理崗位(A類)每月根據領導及同事間的評議得分計算獎金,銷售崗位(B類)則根據銷售額發放提成。稅金則根據獎金和底薪的數額進行計算。由此看出該工資的計算方式是比較穩定的構建演算法,但對工資的每一部分都會根據不同的情況產生不同的演算法,如何將客戶端與變化巨烈的底薪、獎金和稅金計算方式分離呢,這也比較適合用建造者模式。

2 . 問題

我們如何應對這種變化,如何提供一種“封裝機制”來隔離“複雜物件的各個部”的變化,從而保持系統中的“穩定構建演算法”而不隨需求的變化而變化?

3. 解決方案

建造者模式: 將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。

4. 適用性

在以下情況使用Builder模式

•當建立複雜物件的演算法應該獨立於該物件的組成部分以及它們的裝配方式時。

•當構造過程必須允許被構造的物件有不同的表示時。

5. 結 構

此模式結構如下頁上圖所示。


6. 構建模式的組成

• 抽象建造者角色(Builder):為建立一個Product物件的各個部件指定抽象介面,以規範產品物件的各個組成成分的建造。一般而言,此角色規定要實現複雜物件的哪些部分的建立,並不涉及具體的物件部件的建立。
具體建造者(ConcreteBuilder)

1)實現Builder的介面以構造和裝配該產品的各個部件。即實現抽象建造者角色Builder的方法。

2)定義並明確它所建立的表示,即針對不同的商業邏輯,具體化複雜物件的各部分的建立

3)  提供一個檢索產品的介面

4)   構造一個使用Builder介面的物件即在指導者的呼叫下建立產品例項

指導者(Director):呼叫具體建造者角色以建立產品物件的各個部分。指導者並沒有涉及具體產品類的資訊,真正擁有具體產品的資訊是具體建造者物件。它只負責保證物件各部分完整建立或按某種順序建立。

產品角色Product:建造中的複雜物件。它要包含那些定義元件的類,包括將這些元件裝配成產品的介面。

7. 效果

Builder模式的主要效果:

1 ) 它使你可以改變一個產品的內部表示 Builder物件提供給導向器一個構造產品的抽象介面。該介面使得生成器可以隱藏這個產品的表示和內部結構。它同時也隱藏了該產品是如何裝配的。因為產品是通過抽象介面構造的,你在改變該產品的內部表示時所要做的只是定義一個新的生成器。

2) 它將構造程式碼和表示程式碼分開 Builder模式通過封裝一個複雜物件的建立和表示方式提高了物件的模組性。客戶不需要知道定義產品內部結構的類的所有資訊;這些類是不出現在Builder介面中的。每個Concrete Builder包含了建立和裝配一個特定產品的所有程式碼。這些程式碼只需要寫一次;然後不同的Director可以複用它以在相同部件集合的基礎上構作不同的Product。

3 ) 它使你可對構造過程進行更精細的控制 Builder模式與一下子就生成產品的建立型模式不同,它是在導向者的控制下一步一步構造產品的。僅當該產品完成時導向者才從生成器中取回它。因此Builder介面相比其他建立型模式能更好的反映產品的構造過程。這使你可以更精細的控制構建過程,從而能更精細的控制所得產品的內部結構。

8. 實現:

 <?php
 /**
  * 指導者:收銀員
  *
  */
 class DirectorCashier
 {
 	/**
 	 * 收銀餐館員工返回的食物
 	 *
 	 */
 	public function buildFood(Builder $builder) {
 		$builder->buildPart1();
 		$builder->buildPart2();
 	}
 }

 /**
  * 抽象建造者
  *
  */
 abstract class Builder
 {
 	/**
 	 * 建立產品的第一部分
     */
 	public abstract function buildPart1();

 	/**
 	 * 
 	 * 建立產品的第二部分
     */
 	public abstract function buildPart2();
 	
 	/**
 	 * 
 	 *  返回產品
     */
 	public abstract function getProduct();
 }

/**
 * 具體建造者類:餐館員工,返回的套餐是:漢堡兩個+飲料一個
 *
 */
 class ConcreteBuilder1 extends Builder
 {
 	protected $_product = null;//產品物件
 	function __construct(){
 		$this->_product = new Product();
 	}

	/**
 	 * 建立產品的第一部分::漢堡=2
     */
 	public  function buildPart1()
 	{
 		$this->_product->add('Hamburger',2);
 	}
 	/**
 	 * 
 	 * 建立產品的第二部分:
     */
 	public  function buildPart2()
 	{
 		$this->_product->add('Drink', 1);
 	}
 	/**
 	 * 返回產品物件 :
 	 * 
 	 */
 	public function  getProduct()  {
 		return  $this->_product;
 	}
 }

/**
 * 具體建造者類:餐館員工,漢堡1個+飲料2個
 *
 */
 class ConcreteBuilder2 extends Builder
 {
 	protected $_product = null;//產品物件
 	function __construct(){
 		$this->_product = new Product();
 	}

	/**
 	 * 建立產品的第一部分:漢堡
     */
 	public  function buildPart1()
 	{
 		$this->_product->add('Hamburger', 1);
 	}
 	/**
 	 * 
 	 * 建立產品的第二部分:drink=2
     */
 	public  function buildPart2()
 	{
 		$this->_product->add('Drink', 2);
 	}
 	/**
 	 * 返回產品物件 :
 	 * 
 	 */
 	public function  getProduct()  {
 		return  $this->_product;
 	}
 }
/**
  * 產品類
  */
 class Product
 {
 	public $products = array();
 	/**
 	 * 新增具體產品
 	 */
 	public function add($name,  $value) {
 		$this->products[$name] = $value;
 	}
 	/**
 	 * 給顧客檢視產品
 	 */
 	public function showToClient()
 	{
 		foreach ($this->products as $key => $v) {
 			echo $key , '=' , $v ,'<br>';
 		}
 	}
 }
 
 
 //客戶程式
 class Client
 {
 	/**
 	 * 顧客購買套餐
 	 *
 	 */
 	public  function buy($type) {
 		//指導者,收銀員
 		 $director  = new DirectorCashier(); 
 		 //餐館員工,收銀員
         $class = new ReflectionClass('ConcreteBuilder' .$type );
         $concreteBuilder  = $class->newInstanceArgs();
         //收銀員組合員工返回的食物
         $director->buildFood($concreteBuilder);
         //返回給顧客
         $concreteBuilder->getProduct()->showToClient();
 	}
 }
 
 //測試
 ini_set('display_errors', 'On');
 $c = new Client();
 $c->buy(1);//購買套餐1
 $c->buy(2);//購買套餐1



9. 建造者模式的優點

       首先,建造者模式的封裝性很好。使用建造者模式可以有效的封裝變化,在使用建造者模式的場景中,一般產品類和建造者類是比較穩定的,因此,將主要的業務邏輯封裝在導演類中對整體而言可以取得比較好的穩定性。

       其次,建造者模式很容易進行擴充套件。如果有新的需求,通過實現一個新的建造者類就可以完成,基本上不用修改之前已經測試通過的程式碼,因此也就不會對原有功能引入風險。

10. 建造者模式與工廠模式的區別

      我們可以看到,建造者模式與工廠模式是極為相似的,總體上,建造者模式僅僅只比工廠模式多了一個“導演類”的角色。在建造者模式的類圖中,假如把這個導演類看做是最終呼叫的客戶端,那麼圖中剩餘的部分就可以看作是一個簡單的工廠模式了。

      與工廠模式相比,建造者模式一般用來建立更為複雜的物件,因為物件的建立過程更為複雜,因此將物件的建立過程獨立出來組成一個新的類——導演類。也就是說,工廠模式是將物件的全部建立過程封裝在工廠類中,由工廠類向客戶端提供最終的產品;而建造者模式中,建造者類一般只提供產品類中各個元件的建造,而將具體建造過程交付給導演類。由導演類負責將各個元件按照特定的規則組建為產品,然後將組建好的產品交付給客戶端。

11. 總結

      建造者模式與工廠模式類似,他們都是建造者模式,適用的場景也很相似。一般來說,如果產品的建造很複雜,那麼請用工廠模式;如果產品的建造更復雜,那麼請用建造者模式。

 

轉自:http://blog.csdn.net/hguisu/article/details/7518060

相關文章