Sobes.tech
Middle

What is the 'Builder' design pattern?

sobes.tech AI

Answer from AI

The "Builder" pattern is a creational design pattern intended for constructing complex objects step by step. It separates the construction process of a complex object from its representation so that the same construction process can create different representations.

Working principles:

  1. Separation: Separates the logic of creating an object from the object itself.
  2. Delegation: Client code (Director) delegates the creation of the object to a separate class (Builder).
  3. Step-by-step creation: The object is assembled from components using a set of builder methods.

Main participants:

  • Product: The complex object we want to build. It is independent of the creation process.
  • Builder: An abstract interface defining the steps for building the Product.
  • ConcreteBuilder: A concrete implementation of Builder that implements the building steps and provides a method to get the finished Product.
  • Director (Optional): Defines the order of calling the building steps. It is optional; client code can interact directly with ConcreteBuilder.

Advantages:

  • Allows creating objects with many optional parameters more readably than using constructors with many arguments or setter chains.
  • Enables creating different variations of the same object using the same construction process.
  • Hides the internal structure of the object being created.

Disadvantages:

  • May increase the number of classes in the project (Builder, ConcreteBuilder, Product, Director).

Example usage: Creating a Pizza class object with various ingredients.

// Product
public class Pizza {
    private String dough;
    private String sauce;
    private String cheese;
    private List<String> toppings;

    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; }

    @Override
    public String toString() {
        return "Pizza with dough=" + dough + ", sauce=" + sauce + ", cheese=" + cheese + ", toppings=" + toppings;
    }
}
// Builder
public interface PizzaBuilder {
    void buildDough();
    void buildSauce();
    void buildCheese();
    void addTopping(String topping);
    Pizza getPizza();
}
// ConcreteBuilder
public class MargheritaPizzaBuilder implements PizzaBuilder {
    private Pizza pizza;

    public MargheritaPizzaBuilder() {
        this.pizza = new Pizza();
        this.pizza.setToppings(new ArrayList<>()); // Initialize toppings list
    }

    @Override
    public void buildDough() {
        pizza.setDough("Thin crust");
    }

    @Override
    public void buildSauce() {
        pizza.setSauce("Tomato sauce");
    }

    @Override
    public void buildCheese() {
        pizza.setCheese("Mozzarella");
    }

    @Override
    public void addTopping(String topping) {
        // Margherita does not add toppings except the basic ones
    }

    @Override
    public Pizza getPizza() {
        return this.pizza;
    }
}
// Director (Optional)
public class Kitchen {
    private PizzaBuilder pizzaBuilder;

    public void setPizzaBuilder(PizzaBuilder pb) {
        pizzaBuilder = pb;
    }

    public Pizza getPizza() {
        return pizzaBuilder.getPizza();
    }

    public void constructPizza() {
        pizzaBuilder.buildDough();
        pizzaBuilder.buildSauce();
        pizzaBuilder.buildCheese();
        // Other methods can be called, e.g., addTopping() in another builder
    }
}

Usage:

// Client code
Kitchen kitchen = new Kitchen();
PizzaBuilder margheritaBuilder = new MargheritaPizzaBuilder();

kitchen.setPizzaBuilder(margheritaBuilder);
kitchen.constructPizza();

Pizza margherita = kitchen.getPizza();
System.out.println(margherita); // Output: Pizza with dough=Thin crust, sauce=Tomato sauce, cheese=Mozzarella, toppings=[]
What is the 'Builder' design pattern? — QA / QA… - sobes.tech