一、简介
无论是在现实世界中还是在软件系统中,都存在一些复杂的对象,它们拥有多个组成部分,如汽车,它包括车轮、方向盘、发送机等各种部件。而对于大多数用户而言,无须知道这些部件的装配细节,也几乎不会使用单独某个部件,而是使用一辆完整的汽车,可以通过建造者模式对其进行设计与描述,建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节。
复杂对象相当于一辆有待建造的汽车,而对象的属性相当于汽车的部件,建造产品的过程就相当于组合部件的过程。由于组合部件的过程很复杂,因此,这些部件的组合过程往往被“外部化”到一个称作建造者的对象里,建造者返还给客户端的是一个已经建造完毕的完整产品对象,而用户无须关心该对象所包含的属性以及它们的组装方式,这就是建造者模式的模式动机。
建造者模式(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、注意事项:
与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。