1. Definition
The Builder Design Pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations.
2. Problem Statement
Imagine you are creating a “Meal” class with several items (e.g., burger, drink, dessert). There are many varieties and options for each item. If constructors are used to create the meal, the constructor might get too many parameters, and it becomes hard to understand the arrangement and selection of parameters.
3. Solution
The Builder pattern suggests moving the construction logic of the complex object into a separate “builder” class. This class provides methods for setting parts of the complex object step by step. Once the product is built, the client can retrieve the result from the builder.
4. Real-World Use Cases
1. Building complex meal menus in restaurants.
2. Constructing complex documents, like HTML or XML representations.
3. GUI libraries where you might want to construct a complex widget step-by-step.
5. Implementation Steps
1. Create an abstract “Builder” class with methods to define parts of the complex object.
2. Create concrete builder classes for each variant of the complex object.
3. Create a “Director” class that constructs the complex object using the builder object.
4. Client code creates a builder object, passes it to the director, and then retrieves the final product from the builder.
6. Implementation
// Product class
public class Meal {
private String burger;
private String drink;
private String dessert;
public void setBurger(String burger) {
this.burger = burger;
}
public void setDrink(String drink) {
this.drink = drink;
}
public void setDessert(String dessert) {
this.dessert = dessert;
}
@Override
public String toString() {
return "Meal [Burger=" + burger + ", Drink=" + drink + ", Dessert=" + dessert + "]";
}
}
// Abstract Builder
public abstract class MealBuilder {
protected Meal meal = new Meal();
public abstract void buildBurger();
public abstract void buildDrink();
public abstract void buildDessert();
public Meal getMeal() {
return meal;
}
}
// Concrete Builders
public class VegMealBuilder extends MealBuilder {
@Override
public void buildBurger() {
meal.setBurger("Veg Burger");
}
@Override
public void buildDrink() {
meal.setDrink("Coke");
}
@Override
public void buildDessert() {
meal.setDessert("Ice Cream");
}
}
public class NonVegMealBuilder extends MealBuilder {
@Override
public void buildBurger() {
meal.setBurger("Chicken Burger");
}
@Override
public void buildDrink() {
meal.setDrink("Pepsi");
}
@Override
public void buildDessert() {
meal.setDessert("Cake");
}
}
// Director
public class Waiter {
private MealBuilder mealBuilder;
public void setMealBuilder(MealBuilder mb) {
mealBuilder = mb;
}
public Meal getMeal() {
return mealBuilder.getMeal();
}
public void constructMeal() {
mealBuilder.buildBurger();
mealBuilder.buildDrink();
mealBuilder.buildDessert();
}
}
// Client code
public class Restaurant {
public static void main(String[] args) {
Waiter waiter = new Waiter();
MealBuilder vegMealBuilder = new VegMealBuilder();
waiter.setMealBuilder(vegMealBuilder);
waiter.constructMeal();
Meal vegMeal = waiter.getMeal();
System.out.println(vegMeal);
MealBuilder nonVegMealBuilder = new NonVegMealBuilder();
waiter.setMealBuilder(nonVegMealBuilder);
waiter.constructMeal();
Meal nonVegMeal = waiter.getMeal();
System.out.println(nonVegMeal);
}
}
Output:
Meal [Burger=Veg Burger, Drink=Coke, Dessert=Ice Cream] Meal [Burger=Chicken Burger, Drink=Pepsi, Dessert=Cake]
Explanation:
In this example, we've used the Builder pattern to construct different types of meals. Each meal is composed of different items (burger, drink, dessert). The construction process is abstracted into builders (VegMealBuilder and NonVegMealBuilder), and the Waiter acts as the director that orchestrates the building process. The Restaurant (client) can now create different meals using the same construction process.
7. When to use?
Use the Builder pattern when:
1. The algorithm for creating a complex object should be independent of the parts that make up the object and how they’re assembled.
2. The construction process must allow different representations of the constructed object.
3. Object creation involves a lot of optional components or configurations.