1<?php
2
3namespace RefactoringGuru\Builder\Conceptual;
4
5/**
6 * The Builder interface specifies methods for creating the different parts of
7 * the Product objects.
8 */
9interface Builder
10{
11 public function producePartA(): void;
12
13 public function producePartB(): void;
14
15 public function producePartC(): void;
16}
17
18/**
19 * The Concrete Builder classes follow the Builder interface and provide
20 * specific implementations of the building steps. Your program may have several
21 * variations of Builders, implemented differently.
22 */
23class ConcreteBuilder1 implements Builder
24{
25 private $product;
26
27 /**
28 * A fresh builder instance should contain a blank product object, which is
29 * used in further assembly.
30 */
31 public function __construct()
32 {
33 $this->reset();
34 }
35
36 public function reset(): void
37 {
38 $this->product = new Product1();
39 }
40
41 /**
42 * All production steps work with the same product instance.
43 */
44 public function producePartA(): void
45 {
46 $this->product->parts[] = "PartA1";
47 }
48
49 public function producePartB(): void
50 {
51 $this->product->parts[] = "PartB1";
52 }
53
54 public function producePartC(): void
55 {
56 $this->product->parts[] = "PartC1";
57 }
58
59 /**
60 * Concrete Builders are supposed to provide their own methods for
61 * retrieving results. That's because various types of builders may create
62 * entirely different products that don't follow the same interface.
63 * Therefore, such methods cannot be declared in the base Builder interface
64 * (at least in a statically typed programming language). Note that PHP is a
65 * dynamically typed language and this method CAN be in the base interface.
66 * However, we won't declare it there for the sake of clarity.
67 *
68 * Usually, after returning the end result to the client, a builder instance
69 * is expected to be ready to start producing another product. That's why
70 * it's a usual practice to call the reset method at the end of the
71 * `getProduct` method body. However, this behavior is not mandatory, and
72 * you can make your builders wait for an explicit reset call from the
73 * client code before disposing of the previous result.
74 */
75 public function getProduct(): Product1
76 {
77 $result = $this->product;
78 $this->reset();
79
80 return $result;
81 }
82}
83
84/**
85 * It makes sense to use the Builder pattern only when your products are quite
86 * complex and require extensive configuration.
87 *
88 * Unlike in other creational patterns, different concrete builders can produce
89 * unrelated products. In other words, results of various builders may not
90 * always follow the same interface.
91 */
92class Product1
93{
94 public $parts = [];
95
96 public function listParts(): void
97 {
98 echo "Product parts: " . implode(', ', $this->parts) . "\n\n";
99 }
100}
101
102/**
103 * The Director is only responsible for executing the building steps in a
104 * particular sequence. It is helpful when producing products according to a
105 * specific order or configuration. Strictly speaking, the Director class is
106 * optional, since the client can control builders directly.
107 */
108class Director
109{
110 /**
111 * @var Builder
112 */
113 private $builder;
114
115 /**
116 * The Director works with any builder instance that the client code passes
117 * to it. This way, the client code may alter the final type of the newly
118 * assembled product.
119 */
120 public function setBuilder(Builder $builder): void
121 {
122 $this->builder = $builder;
123 }
124
125 /**
126 * The Director can construct several product variations using the same
127 * building steps.
128 */
129 public function buildMinimalViableProduct(): void
130 {
131 $this->builder->producePartA();
132 }
133
134 public function buildFullFeaturedProduct(): void
135 {
136 $this->builder->producePartA();
137 $this->builder->producePartB();
138 $this->builder->producePartC();
139 }
140}
141
142/**
143 * The client code creates a builder object, passes it to the director and then
144 * initiates the construction process. The end result is retrieved from the
145 * builder object.
146 */
147function clientCode(Director $director)
148{
149 $builder = new ConcreteBuilder1();
150 $director->setBuilder($builder);
151
152 echo "Standard basic product:\n";
153 $director->buildMinimalViableProduct();
154 $builder->getProduct()->listParts();
155
156 echo "Standard full featured product:\n";
157 $director->buildFullFeaturedProduct();
158 $builder->getProduct()->listParts();
159
160 // Remember, the Builder pattern can be used without a Director class.
161 echo "Custom product:\n";
162 $builder->producePartA();
163 $builder->producePartC();
164 $builder->getProduct()->listParts();
165}
166
167$director = new Director();
168clientCode($director);