Bird
Raised Fist0

Builder Pattern - Constructing Complex Objects Step by Step

LLDeasyInterview freq: common
Choose your preparation mode4 modes available
🏗️
Builder Pattern - Constructing Complex Objects Step by Step
Use Builder pattern to construct complex objects stepwise, separating construction from representation.
easyDesign Patterns - AppliedFreq: common
Prerequisites:OOP BasicsJava Classes and InterfacesDesign Patterns Fundamentals
⚙️
Functional Requirements
  • Support step-by-step construction of complex objects with optional parts.
  • Separate construction logic from representation to allow multiple product variants.
  • Ensure immutability of the constructed product after build completion.
  • Allow reuse of Builder instances for multiple builds with reset capability.
  • Support thread-safe construction when Builder is used concurrently.
  • Enable extension of Builder for new product variants without modifying existing Builder code.
  • Provide a Director class to encapsulate common construction sequences.
👤
Use Cases
  • Client requests construction of a complex object with multiple optional parts.
  • Director controls the construction sequence using a Builder instance.
  • Builder constructs parts stepwise and returns the final product.
  • Client can create different representations of the product using different Builders.
  • Client can extend Builder to add new parts without modifying existing code.
🧑‍💻
Actors
  • Client
  • Builder
  • Director
❓
Clarifying Questions to Ask
  • How many threads will concurrently use the Builder or Director? Is thread safety required?
  • Should the Builder support partial builds or only complete builds?
  • Are there any constraints on the order of part construction?
  • Is there a pricing or cost calculation strategy for parts during build?
  • What should happen if a required part is missing when build() is called?
  • Can the product be modified after construction or must it be immutable?
  • Should the Director be mandatory or can clients use Builder directly?
Out of Scope
✗ Serialization or persistence of constructed objects.
✗ GUI or user interaction for building objects.
✗ Complex dependency injection frameworks.
🔢
Enumerations
enum ProductType
BASIC | ADVANCED | CUSTOM
Defines product variants to avoid string typos and enable exhaustive switch statements in Director.
enum PartQuality
STANDARD | PREMIUM | DELUXE
Represents quality levels of parts, enabling type-safe quality selection and validation.
📦
Product
4 fields
private final ProductType typeprivate final String partAprivate final String partBprivate final String partC
Immutable complex object constructed stepwise by Builder; holds final product state.
Why thisImmutable class ensures thread safety and consistent state after construction; final fields prevent modification.
Why notMutable POJOs risk inconsistent state; using records limits extensibility and compatibility with older Java versions.
public ProductType getType()Returns the product type.
public String getPartA()Returns part A of the product.
public String getPartB()Returns part B of the product.
public String getPartC()Returns part C of the product.
📦
Builder
4 fields
protected ProductType typeprotected String partAprotected String partBprotected String partC
Abstract class defining stepwise construction methods and build() contract; holds mutable state during build.
Why thisAbstract class allows partial implementation and shared state; supports synchronized build method for thread safety.
Why notInterface cannot hold state or synchronized methods; using abstract class simplifies reuse.
public void reset()Resets builder state to start a new build.
public abstract void buildPartA()Constructs part A of the product.
public abstract void buildPartB()Constructs part B of the product.
public abstract void buildPartC()Constructs part C of the product.
public synchronized Product build()Finalizes and returns the constructed immutable Product; synchronized to ensure thread safety.
📦
ConcreteBuilder
1 fields
private ProductType type
Concrete implementation of Builder for a specific product variant; implements part construction logic.
Why thisConcreteBuilder encapsulates variant-specific build logic; allows multiple variants without changing base Builder.
Why notUsing a single Builder with flags complicates logic and violates single responsibility principle.
public void buildPartA()Builds part A with variant-specific logic.
public void buildPartB()Builds part B with variant-specific logic.
public void buildPartC()Builds part C with variant-specific logic.
public synchronized Product build()Returns the constructed Product instance; synchronized to prevent concurrent build corruption.
📦
Director
1 fields
private Builder builder
Controls construction sequence using Builder; encapsulates common build algorithms.
Why thisDirector centralizes construction sequences, promoting reuse and consistent product creation.
Why notClients could call Builder directly but lose standardization and encapsulation benefits.
public void setBuilder(Builder builder)Associates a Builder instance for construction.
public void constructMinimalProduct()Directs builder to construct minimal viable product.
public void constructFullProduct()Directs builder to construct full-featured product.
📦
Client
0 fields
Uses Director and Builder to create products; can extend Builder for custom variants.
Why thisClient coordinates construction and can customize by extending Builder.
Why notDirect product creation without Builder loses flexibility and separation of concerns.
public Product createProduct(Director director, Builder builder)Coordinates Director and Builder to produce a product.
🔗
Relationships
Director──►Builder[1 to 1]
associationDirector does not own Builder lifecycle; Builder can exist independently
ConcreteBuilder──►Builder[1 to 1]
inheritanceConcreteBuilder extends Builder; inherits mutable build state
Builder◆──►Product[1 to 1]
compositionBuilder creates Product; Product immutable and owned by Builder until build() returns
Client──►Director[1 to 1]
associationClient uses Director but does not own it
Client──►Builder[1 to 1]
associationClient creates or injects Builder; Builder can exist independently
🗄️
products
PK: product_id
product_id (UUID)type (VARCHAR)partA (VARCHAR)partB (VARCHAR)partC (VARCHAR)
Stores immutable product records constructed by Builder.
🗄️
builders
PK: builder_id
builder_id (UUID)type (VARCHAR)state (TEXT)
Stores builder state snapshots for reuse or recovery.
🔌
POST /build
Request
{
  "builderType": "string",
  "parts": {
    "partA": "string",
    "partB": "string",
    "partC": "string"
  }
}
Response
{
  "productId": "UUID",
  "status": "string"
}
Triggers building a product with specified parts using the chosen Builder.
🔌
GET /product/{productId}
Request
{
  "productId": "UUID"
}
Response
{
  "product": {
    "type": "string",
    "partA": "string",
    "partB": "string",
    "partC": "string"
  }
}
Retrieves the constructed product details by ID.
🔌
POST /builder/reset
Request
{
  "builderId": "UUID"
}
Response
{
  "status": "string"
}
Resets the builder state to allow a new build.

Practice

(1/5)
1. In a Card Game Framework, the Deck class has a private constructor and a static synchronized getInstance() method returning the single deck instance. Which design pattern is applied here and why is this the correct choice?
easy
A. Factory Method pattern to create different types of decks dynamically.
B. Singleton pattern to ensure only one deck instance exists across the game.
C. Builder pattern to construct complex deck configurations step-by-step.
D. Prototype pattern to clone decks for multiple players efficiently.

Solution

  1. Step 1: Identify the pattern from constructor and method

    The private constructor prevents external instantiation, and the static synchronized getInstance() method ensures a single shared instance, matching Singleton pattern characteristics.
  2. Step 2: Why Singleton fits this scenario

    In a card game, having a single deck instance avoids inconsistencies and duplication, ensuring all players draw from the same deck state.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Private constructor + static synchronized getInstance() -> Singleton [check]
Hint: Private constructor + static getInstance() -> Singleton [check]
Common Mistakes:
2. In a notification system, a NotificationFactory class has a static method that returns different Notification subclasses (e.g., EmailNotification, SMSNotification) based on input parameters. Which design pattern is applied here and why is it the correct choice?
easy
A. Builder pattern, because it constructs complex notification objects step-by-step.
B. Factory pattern, because it encapsulates object creation and returns subclasses based on input.
C. Singleton pattern, because it ensures only one factory instance exists.
D. Decorator pattern, because it adds responsibilities to notifications dynamically.

Solution

  1. Step 1: Identify the role of NotificationFactory

    The factory method returns different Notification subclasses based on input parameters, encapsulating object creation logic.
  2. Step 2: Eliminate other patterns

    Builder is for stepwise construction, Singleton ensures single instance, Decorator adds behavior dynamically; none match the described creation logic.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Factory pattern encapsulates subclass instantiation based on input [check]
Hint: Factory creates objects based on input type [check]
Common Mistakes:
3. In the Ride Booking system, the FareCalculator class has a private constructor and a static synchronized getInstance() method. Which design pattern is applied here and why is this the correct use case?
easy
A. Factory pattern, because it creates FareCalculator instances on demand.
B. Prototype pattern, to clone FareCalculator instances for thread safety.
C. Singleton pattern, to ensure only one FareCalculator instance calculates fares consistently.
D. Builder pattern, to construct FareCalculator with complex parameters.

Solution

  1. Step 1: Identify the pattern from constructor and method signatures

    The private constructor prevents external instantiation, and the static synchronized getInstance() method ensures a single shared instance, matching Singleton pattern characteristics.
  2. Step 2: Why Singleton fits FareCalculator

    FareCalculator needs consistent fare rules and shared state (e.g., pricing configs). Singleton avoids multiple inconsistent instances and ensures thread-safe access.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Private constructor + synchronized getInstance() -> Singleton pattern [check]
Hint: Private constructor + static getInstance() = Singleton [check]
Common Mistakes:
4. You want to add a WeekendFeeStrategy to calculate message delivery fees differently on weekends. According to SOLID principles, what is the minimum change required in the current design?
medium
A. Add a boolean flag isWeekend in Message and handle fees there.
B. Create a new WeekendFeeStrategy class implementing the existing FeeStrategy interface and inject it where needed.
C. Modify the existing FeeCalculator class to add weekend logic with an if-else block.
D. Create a subclass WeekendFeeCalculator extending FeeCalculator and override fee calculation.

Solution

  1. Step 1: Identify the SOLID principle involved

    Open-Closed Principle (OCP) requires adding new behavior without modifying existing classes.
  2. Step 2: Evaluate options against OCP

    Add a boolean flag isWeekend in Message and handle fees there. adds weekend logic inside Message, violating SRP and mixing concerns; Modify the existing FeeCalculator class to add weekend logic with an if-else block. modifies existing class, violating OCP; Create a subclass WeekendFeeCalculator extending FeeCalculator and override fee calculation. creates subclass but may cause class explosion; Create a new WeekendFeeStrategy class implementing the existing FeeStrategy interface and inject it where needed. adds new strategy class implementing FeeStrategy, adhering to OCP and DIP.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    New strategy class -> OCP and DIP respected [check]
Hint: New strategy class -> Open-Closed Principle [check]
Common Mistakes:
5. To support ElectricCar and ChargingSpot in the existing parking system using Chain of Responsibility, what is the minimum change that respects extensibility and avoids modifying existing classes?
hard
A. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility.COMPATIBILITY_MAP accordingly.
B. Modify Vehicle and Spot classes to add boolean isElectric and isChargingSpot flags, then update existing handlers to check these flags.
C. Create a new ElectricCarHandler in the chain that handles electric cars and charging spots, modifying the chain setup to include it.
D. Add a new method in Vehicle interface for charging compatibility and implement it in ElectricCar, modifying existing handlers to call it.

Solution

  1. Step 1: Identify extensibility requirements

    Adding ElectricCar and ChargingSpot should not require modifying existing classes to avoid breaking OCP.
  2. Step 2: Evaluate options

    Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility.COMPATIBILITY_MAP accordingly. modifies enums and compatibility map, changing existing code; Modify Vehicle and Spot classes to add boolean isElectric and isChargingSpot flags, then update existing handlers to check these flags. adds flags and updates handlers without changing existing class hierarchies; Create a new ElectricCarHandler in the chain that handles electric cars and charging spots, modifying the chain setup to include it. modifies chain setup, which is acceptable but may require modifying chain initialization; Add a new method in Vehicle interface for charging compatibility and implement it in ElectricCar, modifying existing handlers to call it. modifies Vehicle interface, breaking existing implementations.
  3. Step 3: Choose minimal invasive approach

    Modify Vehicle and Spot classes to add boolean isElectric and isChargingSpot flags, then update existing handlers to check these flags. adds minimal changes with flags and updates handlers, avoiding class explosion or interface changes.
  4. Final Answer:

    Option B -> Option B
  5. Quick Check:

    Flags added, existing classes unchanged, handlers updated [check]
Hint: Add flags and update handlers, avoid modifying classes [check]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard