Builder Design Pattern
🎯 Explain Like I'm 5...
Imagine you're making a burger at a restaurant! You don't get the burger all at once - you build it step by step!
🍔 Building a Burger Step by Step:
- • Step 1: Start with a bun 🍞
- • Step 2: Add a patty 🥩
- • Step 3: Add cheese 🧀 (if you want!)
- • Step 4: Add lettuce 🥬 (if you want!)
- • Step 5: Add tomatoes 🍅 (if you want!)
- • Step 6: Add pickles 🥒 (if you want!)
- • Step 7: Put the top bun on, and tada! 🎉
The Builder pattern lets you choose what to add, just like customizing your burger!
🚀 Why Use the Builder Pattern?
- • When you have many optional parts (like burger toppings)
- • When creating an object step by step makes more sense
- • When you want to create different versions of the same thing
- • When constructor has too many parameters (confusing!)
📋 Pattern Purpose
The Builder pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations. It's perfect for creating objects with many optional parameters or when the construction process is complex.
💻 Java Implementations
Example 1: House Builder (Basic Builder)
Building a house with walls, roof, windows, and doors. Shows the classic Builder pattern structure.
// The House class - the product we're buildingpublic class House { // Required parameters private final String foundation; private final String structure; // Optional parameters private final String roof; private final int windows; private final int doors; private final boolean hasGarage; private final boolean hasGarden; private final boolean hasSwimmingPool; // Private constructor - can only be called by Builder private House(HouseBuilder builder) { this.foundation = builder.foundation; this.structure = builder.structure; this.roof = builder.roof; this.windows = builder.windows; this.doors = builder.doors; this.hasGarage = builder.hasGarage; this.hasGarden = builder.hasGarden; this.hasSwimmingPool = builder.hasSwimmingPool; } // Static nested Builder class public static class HouseBuilder { // Required parameters private final String foundation; private final String structure; // Optional parameters - initialized to default values private String roof = "Standard Roof"; private int windows = 4; private int doors = 1; private boolean hasGarage = false; private boolean hasGarden = false; private boolean hasSwimmingPool = false; // Builder constructor with required parameters public HouseBuilder(String foundation, String structure) { this.foundation = foundation; this.structure = structure; } // Setter methods for optional parameters - return 'this' for chaining public HouseBuilder roof(String roof) { this.roof = roof; return this; } public HouseBuilder windows(int windows) { this.windows = windows; return this; } public HouseBuilder doors(int doors) { this.doors = doors; return this; } public HouseBuilder garage(boolean hasGarage) { this.hasGarage = hasGarage; return this; } public HouseBuilder garden(boolean hasGarden) { this.hasGarden = hasGarden; return this; } public HouseBuilder swimmingPool(boolean hasSwimmingPool) { this.hasSwimmingPool = hasSwimmingPool; return this; } // Build method - creates and returns the final House object public House build() { return new House(this); } } @Override public String toString() { return "House{" + "foundation='" + foundation + '\'' + ", structure='" + structure + '\'' + ", roof='" + roof + '\'' + ", windows=" + windows + ", doors=" + doors + ", hasGarage=" + hasGarage + ", hasGarden=" + hasGarden + ", hasSwimmingPool=" + hasSwimmingPool + '}'; } public static void main(String[] args) { // Building a simple house with only required parameters House simpleHouse = new House.HouseBuilder("Concrete", "Wood") .build(); System.out.println("Simple House: " + simpleHouse); // Building a luxury house with all features House luxuryHouse = new House.HouseBuilder("Reinforced Concrete", "Brick") .roof("Tile Roof") .windows(12) .doors(3) .garage(true) .garden(true) .swimmingPool(true) .build(); System.out.println("\nLuxury House: " + luxuryHouse); // Building a custom house - choose only what you want! House customHouse = new House.HouseBuilder("Stone", "Brick") .windows(8) .doors(2) .garden(true) .build(); System.out.println("\nCustom House: " + customHouse); }}Example 2: Pizza Builder (Fluent Builder)
Building a customizable pizza with method chaining. Demonstrates fluent interface design for better readability.
import java.util.ArrayList;import java.util.List;// Pizza class with fluent builder interfacepublic class Pizza { // Enum for pizza size public enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE } // Enum for crust type public enum Crust { THIN, REGULAR, THICK, STUFFED } // Required field private final Size size; // Optional fields private final Crust crust; private final boolean cheese; private final boolean pepperoni; private final boolean mushrooms; private final boolean onions; private final boolean olives; private final boolean extraCheese; private final List<String> customToppings; // Private constructor private Pizza(PizzaBuilder builder) { this.size = builder.size; this.crust = builder.crust; this.cheese = builder.cheese; this.pepperoni = builder.pepperoni; this.mushrooms = builder.mushrooms; this.onions = builder.onions; this.olives = builder.olives; this.extraCheese = builder.extraCheese; this.customToppings = builder.customToppings; } // Static Builder class with fluent interface public static class PizzaBuilder { // Required parameter private final Size size; // Optional parameters with default values private Crust crust = Crust.REGULAR; private boolean cheese = true; // Every pizza has cheese by default! private boolean pepperoni = false; private boolean mushrooms = false; private boolean onions = false; private boolean olives = false; private boolean extraCheese = false; private List<String> customToppings = new ArrayList<>(); // Constructor with required parameters public PizzaBuilder(Size size) { this.size = size; } // Fluent methods for setting options public PizzaBuilder crust(Crust crust) { this.crust = crust; return this; } public PizzaBuilder noCheese() { this.cheese = false; return this; } public PizzaBuilder addPepperoni() { this.pepperoni = true; return this; } public PizzaBuilder addMushrooms() { this.mushrooms = true; return this; } public PizzaBuilder addOnions() { this.onions = true; return this; } public PizzaBuilder addOlives() { this.olives = true; return this; } public PizzaBuilder extraCheese() { this.extraCheese = true; return this; } public PizzaBuilder addCustomTopping(String topping) { this.customToppings.add(topping); return this; } // Build method creates the Pizza public Pizza build() { return new Pizza(this); } } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Pizza {\n"); sb.append(" Size: ").append(size).append("\n"); sb.append(" Crust: ").append(crust).append("\n"); sb.append(" Toppings:\n"); if (cheese) sb.append(" - Cheese\n"); if (extraCheese) sb.append(" - Extra Cheese\n"); if (pepperoni) sb.append(" - Pepperoni\n"); if (mushrooms) sb.append(" - Mushrooms\n"); if (onions) sb.append(" - Onions\n"); if (olives) sb.append(" - Olives\n"); if (!customToppings.isEmpty()) { sb.append(" Custom Toppings:\n"); for (String topping : customToppings) { sb.append(" - ").append(topping).append("\n"); } } sb.append("}"); return sb.toString(); } public static void main(String[] args) { // Simple cheese pizza Pizza cheesePizza = new Pizza.PizzaBuilder(Size.MEDIUM) .build(); System.out.println("=== Cheese Pizza ==="); System.out.println(cheesePizza); // Deluxe pizza with everything! Pizza deluxePizza = new Pizza.PizzaBuilder(Size.LARGE) .crust(Crust.THICK) .extraCheese() .addPepperoni() .addMushrooms() .addOnions() .addOlives() .build(); System.out.println("\n=== Deluxe Pizza ==="); System.out.println(deluxePizza); // Custom pizza with special toppings Pizza customPizza = new Pizza.PizzaBuilder(Size.EXTRA_LARGE) .crust(Crust.THIN) .addPepperoni() .addCustomTopping("Pineapple") .addCustomTopping("Jalapeños") .addCustomTopping("BBQ Chicken") .build(); System.out.println("\n=== Custom Pizza ==="); System.out.println(customPizza); // Vegan pizza (no cheese!) Pizza veganPizza = new Pizza.PizzaBuilder(Size.MEDIUM) .noCheese() .addMushrooms() .addOnions() .addOlives() .addCustomTopping("Vegan Cheese") .addCustomTopping("Artichokes") .build(); System.out.println("\n=== Vegan Pizza ==="); System.out.println(veganPizza); }}Example 3: Computer Builder (Complex Builder)
Building a computer with required and optional components. Shows validation and required vs optional parameters.
// Computer class with validation in builderpublic class Computer { // Required components private final String cpu; private final int ramGB; private final int storageGB; // Optional components private final String gpu; private final boolean hasSSD; private final boolean hasWiFi; private final boolean hasBluetooth; private final int usbPorts; private final String operatingSystem; // Private constructor private Computer(ComputerBuilder builder) { this.cpu = builder.cpu; this.ramGB = builder.ramGB; this.storageGB = builder.storageGB; this.gpu = builder.gpu; this.hasSSD = builder.hasSSD; this.hasWiFi = builder.hasWiFi; this.hasBluetooth = builder.hasBluetooth; this.usbPorts = builder.usbPorts; this.operatingSystem = builder.operatingSystem; } // Getter methods public String getCpu() { return cpu; } public int getRamGB() { return ramGB; } public int getStorageGB() { return storageGB; } public String getGpu() { return gpu; } public boolean hasSSD() { return hasSSD; } public boolean hasWiFi() { return hasWiFi; } public boolean hasBluetooth() { return hasBluetooth; } public int getUsbPorts() { return usbPorts; } public String getOperatingSystem() { return operatingSystem; } // Static Builder class public static class ComputerBuilder { // Required parameters private final String cpu; private final int ramGB; private final int storageGB; // Optional parameters with default values private String gpu = "Integrated Graphics"; private boolean hasSSD = false; private boolean hasWiFi = true; private boolean hasBluetooth = false; private int usbPorts = 2; private String operatingSystem = "None"; // Constructor requires essential components public ComputerBuilder(String cpu, int ramGB, int storageGB) { this.cpu = cpu; this.ramGB = ramGB; this.storageGB = storageGB; } // Optional component methods public ComputerBuilder gpu(String gpu) { this.gpu = gpu; return this; } public ComputerBuilder ssd(boolean hasSSD) { this.hasSSD = hasSSD; return this; } public ComputerBuilder wifi(boolean hasWiFi) { this.hasWiFi = hasWiFi; return this; } public ComputerBuilder bluetooth(boolean hasBluetooth) { this.hasBluetooth = hasBluetooth; return this; } public ComputerBuilder usbPorts(int usbPorts) { this.usbPorts = usbPorts; return this; } public ComputerBuilder operatingSystem(String os) { this.operatingSystem = os; return this; } // Build method with validation public Computer build() { // Validate before building if (ramGB < 4) { throw new IllegalStateException("RAM must be at least 4GB"); } if (storageGB < 128) { throw new IllegalStateException("Storage must be at least 128GB"); } if (usbPorts < 0 || usbPorts > 10) { throw new IllegalStateException("USB ports must be between 0 and 10"); } // If SSD is true and storage is large, recommend more RAM if (hasSSD && storageGB > 512 && ramGB < 16) { System.out.println("Warning: Consider upgrading RAM for better performance!"); } return new Computer(this); } } @Override public String toString() { return "Computer {\n" + " CPU: " + cpu + "\n" + " RAM: " + ramGB + " GB\n" + " Storage: " + storageGB + " GB " + (hasSSD ? "(SSD)" : "(HDD)") + "\n" + " GPU: " + gpu + "\n" + " WiFi: " + (hasWiFi ? "Yes" : "No") + "\n" + " Bluetooth: " + (hasBluetooth ? "Yes" : "No") + "\n" + " USB Ports: " + usbPorts + "\n" + " OS: " + operatingSystem + "\n" + "}"; } public static void main(String[] args) { // Basic computer for office work Computer officePC = new Computer.ComputerBuilder("Intel i5", 8, 256) .ssd(true) .operatingSystem("Windows 11") .build(); System.out.println("=== Office PC ==="); System.out.println(officePC); // Gaming computer with all features Computer gamingPC = new Computer.ComputerBuilder("AMD Ryzen 9", 32, 2000) .gpu("NVIDIA RTX 4090") .ssd(true) .wifi(true) .bluetooth(true) .usbPorts(8) .operatingSystem("Windows 11 Pro") .build(); System.out.println("\n=== Gaming PC ==="); System.out.println(gamingPC); // Development workstation Computer devWorkstation = new Computer.ComputerBuilder("Intel i9", 64, 4000) .gpu("NVIDIA RTX 4070") .ssd(true) .wifi(true) .bluetooth(true) .usbPorts(6) .operatingSystem("Ubuntu Linux") .build(); System.out.println("\n=== Developer Workstation ==="); System.out.println(devWorkstation); // Budget laptop Computer budgetLaptop = new Computer.ComputerBuilder("Intel i3", 8, 512) .ssd(true) .wifi(true) .bluetooth(true) .usbPorts(3) .operatingSystem("Chrome OS") .build(); System.out.println("\n=== Budget Laptop ==="); System.out.println(budgetLaptop); // This will throw an exception - too little RAM! try { Computer invalidPC = new Computer.ComputerBuilder("Intel i5", 2, 256) .build(); } catch (IllegalStateException e) { System.out.println("\n=== Error Creating Invalid PC ==="); System.out.println("Error: " + e.getMessage()); } }}🌍 Real-World Examples
- 1️⃣StringBuilder/StringBuffer: Building strings efficiently in Java
- 2️⃣HTTP Request Builders: Libraries like OkHttp, Apache HttpClient
- 3️⃣Query Builders: SQL query builders in ORMs (Hibernate, JPA)
- 4️⃣UI Builders: Android AlertDialog.Builder, Notification.Builder
- 5️⃣Document Builders: XML/JSON document builders
⚡ When to Use Builder Pattern
- •Too many constructor parameters (more than 4-5)
- •Many optional parameters in object creation
- •Object construction is complex or multi-step
- •Need immutable objects with many fields
- •Want to prevent inconsistent object state during construction
✅ Benefits
- ✓Readable code: Clear and fluent API
- ✓Flexible construction: Choose what to include
- ✓Immutability: Objects are immutable once built
- ✓Validation: Can validate before building final object
- ✓No telescoping constructors: Avoid multiple overloaded constructors
🔑 Key Implementation Points
- 1️⃣Use static nested Builder class inside the target class
- 2️⃣Builder has same fields as target class
- 3️⃣Builder methods return 'this' for method chaining
- 4️⃣Target class constructor is private, accepts Builder
- 5️⃣build() method creates and returns the final object
- 6️⃣Optional: Validate in build() method before creating object
💪 Practice Scenarios
- • Create a Builder for a User class with username, email, phone, address
- • Build an Email class with to, from, subject, body, attachments
- • Design a Car builder with make, model, year, color, features
- • Create a Resume builder with sections like education, experience, skills
- • Build a RestaurantOrder with items, customizations, delivery info