一、簡介
無論是在現實世界中還是在軟件系統中,都存在一些複雜的對象,它們擁有多個組成部分,如汽車,它包括車輪、方向盤、發送機等各種部件。而對於大多數用戶而言,無須知道這些部件的裝配細節,也幾乎不會使用單獨某個部件,而是使用一輛完整的汽車,可以通過建造者模式對其進行設計與描述,建造者模式可以將部件和其組裝過程分開,一步一步創建一個複雜的對象。用戶只需要指定複雜對象的類型就可以得到該對象,而無須知道其內部的具體構造細節。
複雜對象相當於一輛有待建造的汽車,而對象的屬性相當於汽車的部件,建造產品的過程就相當於組合部件的過程。由於組合部件的過程很複雜,因此,這些部件的組合過程往往被“外部化”到一個稱作建造者的對象裏,建造者返還給客戶端的是一個已經建造完畢的完整產品對象,而用戶無須關心該對象所包含的屬性以及它們的組裝方式,這就是建造者模式的模式動機。
建造者模式(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、注意事項:
與工廠模式的區別是:建造者模式更加關注與零件裝配的順序。