在軟體開發的過程中,當遇到一個“複雜的對象”的建立工作,該對象由一定各個部分的子對象用一定的算法構成,由于需求的變化,複雜對象的各個部分經常面臨劇烈的變化,但将它們組合在一起的算法相對穩定。
例子1:買肯德基
典型的兒童餐包括一個主食,一個輔食,一杯飲料和一個玩具(例如漢堡、炸雞、可樂和玩具車)。這些在不同的兒童餐中可以是不同的,但是組合成兒童餐的過程是相同的。
用戶端:顧客,想去買一套套餐(這裡面包括漢堡,可樂,薯條),可以有1号和2号兩種套餐供顧客選擇。
指導者角色:收銀員。知道顧客想要買什麼樣的套餐,并告訴餐館員工去準備套餐。
建造者角色:餐館員工。按照收銀員的要求去準備具體的套餐,分别放入漢堡,可樂,薯條等。
産品角色:最後的套餐,所有的東西放在同一個盤子裡面。
例子2:計算工資:工資的計算一般是:底薪+獎金-稅。但底薪分為一級8000、二級6000、三級4000三個等級。根據崗位不同獎金的發放也不一樣,管理及日常事務處理崗位(a類)每月根據上司及同僚間的評議得分計算獎金,銷售崗位(b類)則根據銷售額發放提成。稅金則根據獎金和底薪的數額進行計算。由此看出該工資的計算方式是比較穩定的建構算法,但對工資的每一部分都會根據不同的情況産生不同的算法,如何将用戶端與變化巨烈的底薪、獎金和稅金計算方式分離呢,這也比較适合用建造者模式。
建造者模式: 将一個複雜對象的建構與它的表示分離,使得同樣的建構過程可以建立不同的表示。
在以下情況使用builder模式
•當建立複雜對象的算法應該獨立于該對象的組成部分以及它們的裝配方式時。
•當構造過程必須允許被構造的對象有不同的表示時。
此模式結構如下頁上圖所示。
• 抽象建造者角色(builder):為建立一個product對象的各個部件指定抽象接口,以規範産品對象的各個組成成分的建造。一般而言,此角色規定要實作複雜對象的哪些部分的建立,并不涉及具體的對象部件的建立。
• 具體建造者(concretebuilder)
1)實作builder的接口以構造和裝配該産品的各個部件。即實作抽象建造者角色builder的方法。
2)定義并明确它所建立的表示,即針對不同的商業邏輯,具體化複雜對象的各部分的建立
3) 提供一個檢索産品的接口
4) 構造一個使用builder接口的對象即在指導者的調用下建立産品執行個體
指導者(director):調用具體建造者角色以建立産品對象的各個部分。指導者并沒有涉及具體産品類的資訊,真正擁有具體産品的資訊是具體建造者對象。它隻負責保證對象各部分完整建立或按某種順序建立。
産品角色(product):建造中的複雜對象。它要包含那些定義元件的類,包括将這些元件裝配成産品的接口。
builder模式的主要效果:
1 ) 它使你可以改變一個産品的内部表示 builder對象提供給導向器一個構造産品的抽象接口。該接口使得生成器可以隐藏這個産品的表示和内部結構。它同時也隐藏了該産品是如何裝配的。因為産品是通過抽象接口構造的,你在改變該産品的内部表示時所要做的隻是定義一個新的生成器。
2) 它将構造代碼和表示代碼分開 builder模式通過封裝一個複雜對象的建立和表示方式提高了對象的子產品性。客戶不需要知道定義産品内部結構的類的所有資訊;這些類是不出現在builder接口中的。每個concrete builder包含了建立和裝配一個特定産品的所有代碼。這些代碼隻需要寫一次;然後不同的director可以複用它以在相同部件集合的基礎上構作不同的product。
3 ) 它使你可對構造過程進行更精細的控制 builder模式與一下子就生成産品的建立型模式不同,它是在導向者的控制下一步一步構造産品的。僅當該産品完成時導向者才從生成器中取回它。是以builder接口相比其他建立型模式能更好的反映産品的構造過程。這使你可以更精細的控制建構過程,進而能更精細的控制所得産品的内部結構。
指導者:收銀員
<?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
* 建立産品的第一部分:漢堡
$this->_product->add('hamburger', 1);
* 建立産品的第二部分:drink=2
$this->_product->add('drink', 2);
産品類:
* 産品類
*/
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>';
}
客戶程式:
<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>
首先,建造者模式的封裝性很好。使用建造者模式可以有效的封裝變化,在使用建造者模式的場景中,一般産品類和建造者類是比較穩定的,是以,将主要的業務邏輯封裝在導演類中對整體而言可以取得比較好的穩定性。
其次,建造者模式很容易進行擴充。如果有新的需求,通過實作一個新的建造者類就可以完成,基本上不用修改之前已經測試通過的代碼,是以也就不會對原有功能引入風險。
我們可以看到,建造者模式與工廠模式是極為相似的,總體上,建造者模式僅僅隻比工廠模式多了一個“導演類”的角色。在建造者模式的類圖中,假如把這個導演類看做是最終調用的用戶端,那麼圖中剩餘的部分就可以看作是一個簡單的工廠模式了。
與工廠模式相比,建造者模式一般用來建立更為複雜的對象,因為對象的建立過程更為複雜,是以将對象的建立過程獨立出來組成一個新的類——導演類。也就是說,工廠模式是将對象的全部建立過程封裝在工廠類中,由工廠類向用戶端提供最終的産品;而建造者模式中,建造者類一般隻提供産品類中各個元件的建造,而将具體建造過程傳遞給導演類。由導演類負責将各個元件按照特定的規則組建為産品,然後将組建好的産品傳遞給用戶端。
建造者模式與工廠模式類似,他們都是建造者模式,适用的場景也很相似。一般來說,如果産品的建造很複雜,那麼請用工廠模式;如果産品的建造更複雜,那麼請用建造者模式。