The Builder pattern is a creational design pattern that separates the construction of a complex object from its representation, allowing the same construction process to create various representations. It is used when we want to create complex objects that have multiple components, which may or may not be required, in a step-by-step manner. This pattern provides a flexible and reusable way to create and represent different types of objects using the same construction process.
In essence, the Builder pattern involves creating a separate Builder class that takes on the responsibility of constructing the object. The Builder contains methods to populate the required fields and optional fields of the object being built. At the end of the construction process, the Builder returns the finished product.
Here is an example to help illustrate the Builder pattern:
Suppose we want to create a Pizza object that has different toppings. We can use the Builder pattern to create a flexible and modular process for constructing our Pizza objects.
First, we create a PizzaBuilder class that takes on the responsibility of constructing our Pizza object. The PizzaBuilder contains a method for each type of topping we want to include in our Pizza object.
public class Pizza {
private String dough = "";
private String sauce = "";
private String cheese = "";
private List<String> toppings = new ArrayList<>();
public void setDough(String dough) {
this.dough = dough;
}
public void setSauce(String sauce) {
this.sauce = sauce;
}
public void setCheese(String cheese) {
this.cheese = cheese;
}
public void setToppings(List<String> toppings) {
this.toppings = toppings;
}
public String getDescription() {
StringBuilder sb = new StringBuilder();
sb.append("Pizza with ").append(this.dough).append(" dough, ");
sb.append(this.sauce).append(" sauce, ").append(this.cheese).append(" cheese and toppings: ");
for (String topping : toppings) {
sb.append(topping).append(", ");
}
sb.setLength(sb.length() - 2);
return sb.toString();
}
}
public class PizzaBuilder {
private Pizza pizza = new Pizza();
public PizzaBuilder addDough(String dough) {
pizza.setDough(dough);
return this;
}
public PizzaBuilder addSauce(String sauce) {
pizza.setSauce(sauce);
return this;
}
public PizzaBuilder addCheese(String cheese) {
pizza.setCheese(cheese);
return this;
}
public PizzaBuilder addTopping(String topping) {
pizza.toppings.add(topping);
return this;
}
public Pizza build() {
return this.pizza;
}
}
The Pizza class represents the object we want to construct, and the PizzaBuilder is responsible for constructing the object. The Pizza object contains fields for dough type, sauce type, cheese type, and an arraylist to hold the toppings. We have created setter methods for each of these fields, which will be used by the PizzaBuilder to construct the object.
The PizzaBuilder contains setter methods for each of the fields that we need to set in order to construct our Pizza object. We have also included a build() method that returns the finished Pizza object.
Now, we can use the PizzaBuilder to construct our Pizza object, as shown below:
Pizza pizza = new PizzaBuilder()
.addDough("Thin")
.addSauce("Tomato")
.addCheese("Mozzarella")
.addTopping("Mushrooms")
.addTopping("Onions")
.build();
System.out.println(pizza.getDescription());
This will create a Pizza object with thin crust, tomato sauce, Mozzarella cheese, mushrooms, and onions as toppings.
The Builder pattern provides a flexible and modular way to construct complex objects in a step-by-step manner. It separates the construction of the object from its representation, allowing different representations to be created using the same construction process. This pattern helps to keep the construction code clean and maintainable, and it makes it easy to add new components to the object being constructed without changing the existing code.