Understanding Builder Design Pattern in Swift
First of all, it is necessary to understand what Design Patterns are. Here is my medium article link for the same.
It covers the intro to Design Patterns in very concise way. Please do check it in case you are completely beginner to design patterns.
Builder is a creational design pattern, which allows constructing complex objects step by step.
Let us understand builder pattern with help of an example. Suppose that we are running a Pizza Shop and our aim is to make delicious pizzas for our customers. We make pizzas using struct Pizza
. The struct Pizza
has size
, base
and toppings
as properties.
struct Pizza {
let size: Size
let base: Base
let toppings: [Topping]
}
Currently we are making only sevenInches
and tenInches
size pizza. We have two options for pizza base thin
and thick
. For toppings we have five different varieties, i.e., pepperoni
, sausage
, mushrooms
, onions
and blackOlives
.
enum Size {
case sevenInches
case tenInches
}
enum Base {
case thin
case thick
}
enum Topping {
case pepperoni
case sausage
case mushrooms
case onions
case blackOlives
}
Now, let us create a pizza of size tenInches
, with thin
base and onions
as toppings. It is very simple. Just create an object of struct Pizza
.
let pizza = Pizza(size: .tenInches, base: .thin, toppings: [.onions])
Now, what if we do not know what the parameters will be. What if the parameter list is too long that we can make a mistake while making entries. Also, what if we want to create a similar pizza with just slight modifications. In that case we still have to specify all the parameters that are unchanged. That will be hell lot of work!!! So, how are we going to overcome these problems? The solution to these problems is Builder Pattern.
Builder Pattern builds object step by step. We create PizzaBuilder
class that has same variables as Pizza
class but here they have private setters.
We have functions to set Size
, Base
and Topping
. At last we have function build
to build our Pizza
.
class PizzaBuilder {
private (set) var size: Size = .sevenInches
private (set) var base: Base = .thick
private (set) var toppings: [Topping] = []
func setSize(_ size: Size) {
self.size = size
}
func setBase(_ base: Base) {
self.base = base
}
func addToppings(_ topping: Topping) {
toppings.append(topping)
}
func build() -> Pizza {
return Pizza(size: size, base: base, toppings: toppings)
}
}
Now, let us build a Pizza
using PizzaBuilder
class.
let builder = PizzaBuilder()
builder.setBase(.thin)
builder.setSize(.tenInches)
builder.addToppings(.mushrooms)
builder.addToppings(.onions)
let pizza = builder.build()
First we have created an object of PizzaBuilder
class and then step by step built the Pizza
. Here, we can see that we have set each parameter individually and if by any chance we don’t set any parameter value then it will take its default value.
Now suppose we also want to create another pizza that has all the properties of the pizza that we have just created above and in addition to that it must also have blackOlives
as Topping
, so in that case we do not need to do extra work because we have the builder
object that we have just created above. We simply add blackOlives
as Topping
using that builder
object.
builder.addToppings(.blackOlives)
let anotherPizza = builder.build()
Hurrah! We have just build anotherPizza
of size tenInches
, with thin
base and onions
, mushrooms
and blackOlives
as toppings.
Builder Pattern Pros
- Builder pattern reduces the parameters of the constructor and makes them highly readable.
- Only fully constructed object will be available to client.
- We can construct objects step by step.
Builder Pattern Cons
- The number of lines and overall complexity of code increases in builder pattern.
When to use Builder Pattern?
When a lot of parameters are needed in the constructor, the builder design pattern can be employed.
If you found this article helpful, kindly share this story, follow me on medium and give applaud 👏. Thank You!