設計模式06--建造者模式(Builder)

一、簡介

無論是在現實世界中還是在軟件系統中,都存在一些複雜的對象,它們擁有多個組成部分,如汽車,它包括車輪、方向盤、發送機等各種部件。而對於大多數用戶而言,無須知道這些部件的裝配細節,也幾乎不會使用單獨某個部件,而是使用一輛完整的汽車,可以通過建造者模式對其進行設計與描述,建造者模式可以將部件和其組裝過程分開,一步一步創建一個複雜的對象。用戶只需要指定複雜對象的類型就可以得到該對象,而無須知道其內部的具體構造細節。

複雜對象相當於一輛有待建造的汽車,而對象的屬性相當於汽車的部件,建造產品的過程就相當於組合部件的過程。由於組合部件的過程很複雜,因此,這些部件的組合過程往往被“外部化”到一個稱作建造者的對象裏,建造者返還給客戶端的是一個已經建造完畢的完整產品對象,而用戶無須關心該對象所包含的屬性以及它們的組裝方式,這就是建造者模式的模式動機。

建造者模式(Builder Pattern):將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。建造者模式是一步一步創建一個複雜的對象,它允許用戶只通過指定複雜對象的類型和內容就可以構建它們,用戶不需要知道內部的具體構建細節。建造者模式屬於對象創建型模式。根據中文翻譯的不同,建造者模式又可以稱爲生成器模式。

二、場景

比如:去肯德基,漢堡、可樂、薯條、炸雞翅等是不變的,而其組合是經常變化的,生成出所謂的"套餐"。

使用場景:
1)需要生成的對象具有複雜的內部結構。
2)需要生成的對象內部屬性本身相互依賴。

建造者模式主要解決在軟件系統中,有時候面臨着"一個複雜對象"的創建工作,其通常由各個部分的子對象用一定的算法構成;由於需求的變化,這個複雜對象的各個部分經常面臨着劇烈的變化,但是將它們組合在一起的算法卻相對穩定。

三、類結構

建造者模式包含如下角色:

角色 簡述
Builder 抽象建造者
ConcreteBuilder 具體建造者
Director 指揮者
Product 產品角色
  • 抽象建造者定義了產品的創建方法和返回方法。
  • 指揮者的作用主要有兩個:一方面它隔離了客戶與生產過程;另一方面它負責控制產品的生成過程。
  • 客戶端只需要知道具體建造者的類型,即可通過指揮者類調用建造者的相關方法,返回一個完整的產品對象

四、UML圖

五、代碼分析

下面以建造汽車爲例。

1、抽象建造者

interface BuilderInterface
{
    public function createVehicle();

    public function addWheel();

    public function addEngine();

    public function addDoors();

    public function getVehicle();
}

2、具體建造者

// 建造大卡車
class TruckBuilder implements BuilderInterface
{
    /**
     * @var Truck
     */
    private $truck;

    public function createVehicle(){
        $this->truck = new Truck();
    }

    public function addDoors(){
        $this->truck->setPart('rightDoor', new Door());
        $this->truck->setPart('leftDoor', new Door());
        $this->truck->setPart('trunkLid', new Door());
    }

    public function addEngine(){
        $this->truck->setPart('truckEngine', new Engine());
    }

    public function addWheel(){
        $this->truck->setPart('wheel1', new Wheel());
        $this->truck->setPart('wheel2', new Wheel());
        $this->truck->setPart('wheel3', new Wheel());
        $this->truck->setPart('wheel4', new Wheel());
        $this->truck->setPart('wheel5', new Wheel());
        $this->truck->setPart('wheel6', new Wheel());
    }

    public function getVehicle(){
        return $this->truck;
    }
}

// 建造小汽車
class CarBuilder implements BuilderInterface
{
    /**
     * @var Car
     */
    private $car;

    public function createVehicle(){
        $this->car = new Car();
    }

    public function addDoors(){
        $this->car->setPart('rightDoor', new Door());
        $this->car->setPart('leftDoor', new Door());
    }

    public function addEngine(){
        $this->car->setPart('engine', new Engine());
    }

    public function addWheel(){
        $this->car->setPart('wheelLF', new Wheel());
        $this->car->setPart('wheelRF', new Wheel());
        $this->car->setPart('wheelLR', new Wheel());
        $this->car->setPart('wheelRR', new Wheel());
    }

    public function getVehicle(){
        return $this->car;
    }
}

3、指揮者

/**
 * Director 類是建造者模式的一部分。 它可以實現建造者模式的接口
 * 並在構建器的幫助下構建一個複雜的對象
 *
 * 您也可以注入許多構建器而不是構建更復雜的對象
 */
class Director
{
    public function build(BuilderInterface $builder)
    {
        $builder->createVehicle();
        $builder->addDoors();
        $builder->addEngine();
        $builder->addWheel();

        return $builder->getVehicle();
    }
}

4、產品角色

// 抽象產品
abstract class Vehicle
{
    /**
     * @var object[]
     */
    private $data = [];

    /**
     * @param string $key
     * @param object $value
     */
    public function setPart($key, $value){
        $this->data[$key] = $value;
    }
}

class Truck extends Vehicle{
}
class Car extends Vehicle{
}

// 產品部件
class Engine{
}
class Wheel{
}
class Door{
}

5、使用案例

$diretor = new Director();
$myTruck = $diretor->build(new TruckBuilder());
$myCar = $diretor->build(new CarBuilder());

var_dump($myTruck);
var_dump($myCar);

輸出結果:

object(Truck)#3 (1) {
  ["data":"Vehicle":private]=>
  array(10) {
    ["rightDoor"]=>
    object(Door)#4 (0) {
    }
    ["leftDoor"]=>
    object(Door)#5 (0) {
    }
    ["trunkLid"]=>
    object(Door)#6 (0) {
    }
    ["truckEngine"]=>
    object(Engine)#7 (0) {
    }
    ["wheel1"]=>
    object(Wheel)#8 (0) {
    }
    ["wheel2"]=>
    object(Wheel)#9 (0) {
    }
    ["wheel3"]=>
    object(Wheel)#10 (0) {
    }
    ["wheel4"]=>
    object(Wheel)#11 (0) {
    }
    ["wheel5"]=>
    object(Wheel)#12 (0) {
    }
    ["wheel6"]=>
    object(Wheel)#13 (0) {
    }
  }
}
object(Car)#14 (1) {
  ["data":"Vehicle":private]=>
  array(7) {
    ["rightDoor"]=>
    object(Door)#15 (0) {
    }
    ["leftDoor"]=>
    object(Door)#16 (0) {
    }
    ["engine"]=>
    object(Engine)#17 (0) {
    }
    ["wheelLF"]=>
    object(Wheel)#18 (0) {
    }
    ["wheelRF"]=>
    object(Wheel)#19 (0) {
    }
    ["wheelLR"]=>
    object(Wheel)#20 (0) {
    }
    ["wheelRR"]=>
    object(Wheel)#21 (0) {
    }
  }
}

六、特點

1、優點:

  • 在建造者模式中, 客戶端不必知道產品內部組成的細節,將產品本身與產品的創建過程解耦,使得相同的創建過程可以創建不同的產品對象。
  • 每一個具體建造者都相對獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者, 用戶使用不同的具體建造者即可得到不同的產品對象 。
  • 可以更加精細地控制產品的創建過程 。將複雜產品的創建步驟分解在不同的方法中,使得創建過程更加清晰,也更方便使用程序來控制創建過程。
  • 增加新的具體建造者無須修改原有類庫的代碼,指揮者類針對抽象建造者類編程,系統擴展方便,符合“開閉原則”。

2、缺點:

  • 產品必須有共同點,範圍有限制。
  • 如內部變化複雜,會有很多的建造類。

3、注意事項:
與工廠模式的區別是:建造者模式更加關注與零件裝配的順序。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章