設計模式(三)建造者模式Builder(創建型)

1. 概述

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

       例子1:買肯德基

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

       1.jpg

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

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

2 . 問題

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

3. 解決方案

建造者模式: 將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。

4. 適用性

在以下情況使用Builder模式

?當創建復雜對象的算法應該獨立于該對象的組成部分以及它們的裝配方式時。

?當構造過程必須允許被構造的對象有不同的表示時。

5. 結 構

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

2.jpg

6. 構建模式的組成

? 抽象建造者角色(Builder):為創建一個Product對象的各個部件指定抽象接口,以規范產品對象的各個組成成分的建造。一般而言,此角色規定要實現復雜對象的哪些部分的創建,并不涉及具體的對象部件的創建。
具體建造者(ConcreteBuilder)

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

2)定義并明確它所創建的表示,即針對不同的商業邏輯,具體化復雜對象的各部分的創建

3)  提供一個檢索產品的接口

4)   構造一個使用Builder接口的對象即在指導者的調用下創建產品實例

指導者(Director):調用具體建造者角色以創建產品對象的各個部分。指導者并沒有涉及具體產品類的信息,真正擁有具體產品的信息是具體建造者對象。它只負責保證對象各部分完整創建或按某種順序創建。

產品角色(Product):建造中的復雜對象。它要包含那些定義組件的類,包括將這些組件裝配成產品的接口。

7. 效果

Builder模式的主要效果:

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

2) 它將構造代碼和表示代碼分開 Builder模式通過封裝一個復雜對象的創建和表示方式提高了對象的模塊性??蛻舨恍枰蓝x產品內部結構的類的所有信息;這些類是不出現在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>';  
        }  
    }  
 }
  1. 客戶程序:  

<pre name="code" class="php"> //客戶程序  
 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</pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>

 

9. 建造者模式的優點

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

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

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

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

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

11. 總結

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

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

原創文章,作者:s19930811,如若轉載,請注明出處:http://www.www58058.com/2947

(0)
s19930811s19930811
上一篇 2015-06-25 10:08
下一篇 2015-06-25 10:12

相關推薦

  • bash的命令執行狀態返回值及命令行展開詳解

    命令執行狀態返回值及命令行展開是bash的2個基本特性,其詳細特點及應用如下:   一、命令執行的狀態結果:bash通過狀態返回值來輸出此結果                 成功:0             …

    Linux干貨 2016-11-06
  • Linux Basics–part4

    1、復制/etc/skel目錄為/home/tuser1,要求/home/tuser1及其內部文件的屬組和其它用戶均沒有任何訪問權限 ~]# cp -rf /etc/skel/ /home/tuser1 && chmod -R go=— /home/tuser1 [root@ronny1 ~]# ll -d /home/tuser…

    Linux干貨 2017-08-07
  • lvm簡要及基本操作

        LVM( Logical Volume Manage,邏輯 邏輯卷管理)LVM將一個或多個硬盤的分區在邏輯上集合,相當于一個大硬盤來使用,當硬盤的空間不夠使用的時候,可以繼續將其它的硬盤的分區加入其中,這樣可以實現磁盤空間的動態管理,相對于普通的磁盤分區有很大的靈活性。  &nbs…

    Linux干貨 2016-05-23
  • Tomcat 全系列發現嚴重安全漏洞

    據 Tomcat 安全組確認,Tomcat 全系列產品均被發現嚴重安全漏洞:CVE-2014-0227 請求夾帶漏洞。 級別:嚴重 受影響版本: Apache Tomcat 8.0.0-RC1 to 8.0.8 Apache Tomcat 7.0.0 to 7.0.54 Apache Tomcat 6.0.0 to 6.0.41 描述:可以通過構造一個截斷請…

    Linux干貨 2015-02-11
  • 管理SYSTEMd

    核心概念unit,systemctl管理系統服務,

    Linux干貨 2018-01-04
  • shell腳本編程2

    shell腳本編程2   今天繼續學習了shell腳本編程,續之前寫的腳本編程1。上次主要學了編程的基礎,腳本格式,變量類型及變量的使用,條件測試,數字運算并且進行了部分練習,因為這部分邏輯思維能力要求不是很高,因此記憶起來難度不是特別大。   從今天開始便進入了流程控制以及函數、數組、高級字符串操作、高級變量等內容的學習,總體來說難度和…

    Linux干貨 2016-08-16
欧美性久久久久