Understanding the Builder Design Pattern in PHP

The Problem

The Builder Pattern is a software design pattern which helps to simplify creation of complex objects by reducing the number of parameters in the object constructor.
This pattern uses a builder object to collect parameters of the complex object and return the final constructed object.

Imagine we have a car object with many attributes.
The following is the class definition and object instantiation code:

class Car {
	protected $make;
	protected $model;
	protected $badge;
	protected $series;
	protected $year;
	protected $kilometers;
	protected $bodyType;
	protected $engineSize;
	protected $fuelType;
	protected $cylinders;
	protected $transmission;
	protected $color;
	
	public function __construct($make, $model, $badge, $series, $year, $kilometers, $bodyType, $engineSize,
					   $fuelType, $cylinders, $transmission, $color) {
		$this->make 		= $make;
		$this->model 		= $model;
		$this->badge 		= $badge;
		$this->series 		= $series;
		$this->year 		= $year;
		$this->kilometers 	= $kilometers;
		$this->bodyType 	= $bodyType;
		$this->engineSize 	= $engineSize;
		$this->fuelType		= $fuelType;
		$this->cylinders 	= $cylinders;
		$this->transmission = $transmission;
		$this->color 		= $color;

	}	
}

$carObj = new car('Toyota', 'Camry', 'Altise', 'ACV36R', '2005', '60000', 'Sedan', '2400', 'Petrol', '4', 'Auto', 'White');
var_dump($carObj);

As you can see, when we want to create a Car object we have to pass all those attributes to the constructor.
In addition, what if we want to skip over some attributes and use the default value? For instance, we want to have default value of ‘Sedan’, ‘Petrol’, and ‘4’ for bodyType, fuelType, and cylinders respectively.
The constructor would look like:

public function __construct($make, $model, $badge, $series, $year, $kilometers, $bodyType = 'Sedan', $engineSize, $fuelType = 'Petrol', $cylinders = 4, $transmission, $color) { ... }

But how would our object instantiation look like?

The Solution: Builder Pattern

Instead of instantiating Car object directly we create a builder object called CarBuilder and let that handle our object creation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
class CarBuilder {
	protected $make;
	protected $model;
	protected $badge;
	protected $series;
	protected $year;
	protected $kilometers;
	protected $bodyType = 'Sedan';
	protected $engineSize;
	protected $fuelType = 'Petrol';
	protected $cylinders = 4;
	protected $transmission;
	protected $color;

	public function __construct() { }

	public function setMake($make) {
		$this->make = $make;
		return $this;
	}

	public function setModel($model) {
		$this->model = $model;
		return $this;
	}

	public function setBadge($badge) {
		$this->badge = $badge;
		return $this;
	}

	public function setSeries($series) {
		$this->series = $series;
		return $this;
	}

	public function setYear($year) {
		$this->year = $year;
		return $this;
	}

	public function setKilometers($kilometers) {
		$this->kilometers = $kilometers;
		return $this;
	}

	public function setBodyType($bodyType) {
		$this->bodyType = $bodyType;
		return $this;
	}

	public function setEngineSize($engineSize) {
		$this->engineSize = $engineSize;
		return $this;
	}

	public function setFuelType($fuelType) {
		$this->fuelType = $fuelType;
		return $this;
	}

	public function setCylinders($cylinders) {
		$this->cylinders = $cylinders;
		return $this;
	}

	public function setTransmission($transmission) {
		$this->transmission = $transmission;
		return $this;
	}

	public function setColor($color) {
		$this->color = $color;
		return $this;
	}

	public function build() {
		return new Car($this->make, $this->model, $this->badge, $this->series, $this->year, $this->kilometers, $this->bodyType,
					   $this->engineSize, $this->fuelType, $this->cylinders, $this->transmission, $this->color);
	}
}

$builderObj = new CarBuilder();

$carObj = $builderObj->setMake('Toyota')
	      ->setModel('Camry')
	      ->setBadge('Altise')
	      ->setSeries('ACV36R')
	      ->setYear('2005')
	      ->setKilometers('60000')
	      ->setEngineSize('2400')
	      ->setTransmission('Auto')
	      ->setColor('White')
	      ->build();

var_dump($carObj);

Advantages and Disadvantages

Advantages

  • Clean and easier to read code
  • Decoupled and more encapsulated code (have you noticed the use of Factory Pattern inside build() function?)
  • You can easily skip attributes that are not needed for specific objects and use the default value instead
  • Avoiding too many arguments in the constructor

Disadvantages

  • It requires writing more code