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
40 min ยท medium
medium

Design a system to build a complex Product object with 5 configuration levels and 4 optional gates. Each gate can be enabled or disabled independently. Pricing depends on selected levels and gates. The builder must support stepwise construction and be thread-safe for concurrent usage.

  • Product has 5 configuration levels, each with distinct parameters.
  • There are 4 gates that can be toggled on/off independently.
  • Pricing is calculated based on selected levels and enabled gates.
  • Builder must allow step-by-step construction with method chaining.
  • The builder will be used concurrently by multiple threads.
  • Final Product object is immutable once built.
๐Ÿ“‹
Guided Steps
1/5
Step 1
Define the Product class with immutable state
Design the Product class to hold configuration levels, gates, and pricing. Ensure immutability after construction.
๐Ÿ’ก Hint
Use private final fields and no setters. Initialize all fields in constructor. Mark class final to prevent subclassing.
final class Product with private final fields for levels, gates, and price - immutability enforced by no setters and final fields in Java
Mid: Uses Builder pattern to separate construction from representation. Senior: Explains Java immutability guarantees via final fields and no setters to prevent visibility issues.
โš ๏ธCommon Wrong Answer
Mutable Product with public setters leads to inconsistent state visible to other threads without synchronization.
Step 2
Design the Builder interface and concrete Builder class
Create a Builder interface defining stepwise methods and a concrete Builder implementing it to construct Product.
๐Ÿ’ก Hint
Use interface for method signatures without state; concrete Builder holds mutable state fields. Return Builder type for method chaining.
Builder interface defines method signatures only; ConcreteBuilder class holds mutable fields and implements Builder - interface cannot hold instance state in Java
Mid: Recognizes separation of interface and implementation for extensibility. Senior: Explains interface's inability to hold state and why mutable fields must be in concrete Builder class.
โš ๏ธCommon Wrong Answer
Putting mutable fields in interface leads to compilation error since Java interfaces cannot hold instance state.
Step 3
Implement thread-safe stepwise construction in Builder
Ensure the Builder supports concurrent usage by multiple threads without corrupting internal state.
๐Ÿ’ก Hint
Use synchronized methods or locks to protect mutable fields during stepwise configuration. Avoid returning partially built Product.
synchronized methods in ConcreteBuilder to atomically update mutable fields and build Product - prevents race conditions on shared mutable state
Mid: Uses synchronized keyword on builder methods. Senior: Traces thread interleaving where Thread 1 updates level, Thread 2 reads stale state without synchronization causing inconsistent Product.
โš ๏ธCommon Wrong Answer
No synchronization leads to race conditions where multiple threads corrupt builder state causing invalid Product construction.
Step 4
Implement pricing calculation based on levels and gates
Design pricing logic that calculates total price based on selected configuration levels and enabled gates.
๐Ÿ’ก Hint
Use enums for levels and gates with associated price values. Calculate price in build() method after all selections are made.
Use enum Level and Gate with fixed price fields - enums provide type safety and prevent string typos in pricing logic
Mid: Uses enums for levels and gates. Senior: Explains how enums enable exhaustive switch statements and prevent runtime bugs from invalid strings.
โš ๏ธCommon Wrong Answer
Using String constants for levels/gates causes typo bugs and runtime failures in pricing calculation.
Step 5
Ensure extensibility and adherence to SOLID principles
Design the system so new configuration levels or gates can be added without modifying existing classes.
๐Ÿ’ก Hint
Use open-closed principle: define interfaces and enums that can be extended. Avoid hardcoding pricing logic inside Builder.
Open-closed principle enforced by using enums and interfaces; pricing logic separated into PricingStrategy interface implemented externally
Mid: Recognizes need for open-closed principle. Senior: Explains how separating pricing logic into strategy pattern avoids modifying Builder when adding new levels/gates.
โš ๏ธCommon Wrong Answer
Hardcoding pricing in Builder violates open-closed principle, requiring Builder modification for new features.
๐Ÿ”Œ
API Design Challenge
Design a thread-safe Builder API in Java that supports stepwise construction of a complex Product with multiple configuration levels and optional gates. The API should allow enabling/disabling gates independently, support method chaining, and ensure immutability of the final Product.
  • Use a Builder interface with stepwise methods returning Builder for fluent chaining.
  • Implement a ConcreteBuilder class with synchronized methods to ensure thread safety.
  • Use enums for configuration levels and gates with associated pricing to avoid string errors.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • final immutable Product class with private final fields
  • Builder interface without instance state
  • ConcreteBuilder with synchronized methods for thread safety
  • Enums for Level and Gate with price fields
  • build() method returning immutable Product
Good to Have - 30%
  • PricingStrategy interface for pricing logic separation
  • Fluent method chaining in Builder
  • Use of open-closed principle for extensibility
Bonus - 10%
  • Detailed concurrency explanation with thread interleaving
  • Use of volatile if needed for visibility in complex scenarios
Mutable Product class with public setters causing visibility and consistency bugs leading to unpredictable behavior in concurrent environments
Mutable fields in interface causing compilation errors because Java interfaces cannot hold instance state
No synchronization in Builder leading to race conditions and corrupted state causing invalid Product construction
Using String constants for levels/gates causing typo bugs and runtime failures due to lack of type safety
Hardcoding pricing logic inside Builder violating open-closed principle, requiring Builder modification for new features and reducing maintainability
โœ…
Model Answer
โ–ผ Reveal

The solution uses a final immutable Product class with private final fields to guarantee thread-safe visibility and immutability. A Builder interface defines stepwise construction methods without state, while a ConcreteBuilder class holds mutable fields and synchronizes methods to ensure thread safety during concurrent usage. Enums Level and Gate encapsulate configuration options with fixed price fields, preventing runtime string errors. Pricing logic is separated into a PricingStrategy interface to enforce the open-closed principle. Builder methods return Builder type for fluent chaining, and the build() method returns a fully constructed immutable Product instance.

  • Use of final keyword on Product class and fields to enforce immutability and thread-safe visibility
  • Separation of Builder interface (stateless) and ConcreteBuilder (stateful) to comply with Java interface constraints
  • Synchronized methods in ConcreteBuilder to prevent race conditions during concurrent builder usage
  • Enums for Level and Gate to provide type safety and prevent runtime errors from string typos
  • PricingStrategy interface to adhere to open-closed principle and allow extensible pricing logic
  • final immutable Product class with private final fields and no setters to guarantee thread-safe visibility
  • Builder interface defines stepwise construction methods without state, enabling multiple implementations
  • ConcreteBuilder class holds mutable fields, uses synchronized methods to ensure atomic updates and thread safety
  • Enums Level and Gate with fixed price fields provide type safety and prevent runtime string errors
  • PricingStrategy interface separates pricing calculation from Builder, enforcing open-closed principle
  • Builder methods return Builder type for fluent method chaining
  • build() method returns fully constructed immutable Product instance
  • Use of synchronized keyword on mutable state methods to prevent race conditions during concurrent builder usage
  • Avoid exposing mutable internal state to clients to maintain immutability guarantees
  • Use of final keyword on classes and fields to enforce immutability and prevent subclassing

Practice

(1/5)
1. In a command pattern implementation for undo/redo, a Command interface declares an execute() method and concrete commands implement this interface to encapsulate operations. Which OOP concept is primarily applied here and why is this the correct use case?
easy
A. Encapsulation, because each command object hides the details of the operation it performs
B. Inheritance, because concrete commands extend a base class to reuse code
C. Polymorphism, because commands override execute() to provide different behaviors
D. Abstraction, because the interface hides implementation details from the client

Solution

  1. Step 1: Identify the role of Command objects

    Each command encapsulates all information needed to perform an action, hiding the operation's details from the invoker.
  2. Step 2: Differentiate encapsulation from other concepts

    While polymorphism and abstraction are involved, the key concept is encapsulation because commands package the action and its parameters as an object.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Command objects encapsulate operations and data for undo/redo [check]
Hint: Command pattern encapsulates operations as objects [check]
Common Mistakes:
2. Given a Vehicle abstraction that requires both state (e.g., speed) and behavior (e.g., accelerate()), should Vehicle be an abstract class or an interface in Java, and why?
easy
A. Interface, because it allows multiple inheritance and can hold state variables.
B. Interface, because abstract classes cannot define method signatures without implementation.
C. Abstract class, because it can hold shared state and provide default behavior implementations.
D. Abstract class, because interfaces cannot have any methods related to behavior.

Solution

  1. Step 1: Analyze state and behavior requirements

    Since Vehicle needs to hold state like speed and provide default behavior like accelerate(), it requires fields and method implementations.
  2. Step 2: Choose between abstract class and interface

    Java interfaces cannot hold instance state (fields) prior to Java 8 default methods, and even then, no instance variables. Abstract classes can hold state and provide default method implementations, making them suitable here.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    State + default behavior -> abstract class, not interface [check]
Hint: State needs fields -> abstract class; interfaces lack instance variables [check]
Common Mistakes:
3. In a Java class hierarchy, a base class defines a final method that calls several abstract methods which subclasses must implement. Which design pattern is this an example of?
easy
A. Template Method Pattern, because the base class defines the algorithm skeleton and subclasses fill in steps.
B. Strategy Pattern, because subclasses provide interchangeable algorithms.
C. Factory Method Pattern, because the base class creates objects via subclass overrides.
D. Decorator Pattern, because behavior is added dynamically by subclasses.

Solution

  1. Step 1: Identify the role of the final method

    The base class's final method defines the fixed algorithm skeleton, preventing subclasses from changing the overall flow.
  2. Step 2: Recognize abstract methods as extension points

    Subclasses implement abstract methods to fill in specific steps, matching the Template Method pattern's intent.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Base class controls algorithm flow; subclasses customize steps [check]
Hint: Final method fixes flow; subclasses fill steps [check]
Common Mistakes:
4. In a multi-threaded environment, if the addProductToCart() method is not synchronized, which exact sequence of events can cause the cart to contain duplicate entries for the same product?
medium
A. Thread 1 checks if product exists -> false, context switch; Thread 2 checks -> false; both add product concurrently.
B. Thread 1 adds product, Thread 2 removes product simultaneously causing inconsistency.
C. Thread 1 updates quantity, Thread 2 reads stale quantity due to lack of volatile keyword.
D. Thread 1 locks cart, Thread 2 waits but eventually overwrites product quantity.

Solution

  1. Step 1: Identify check-then-act race condition

    Both threads check if product exists and see false before either adds it, due to lack of synchronization.
  2. Step 2: Both threads add product concurrently causing duplicates

    Because the add operation is not atomic, both add the product, resulting in duplicates.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Check-then-act gap without synchronization -> duplicate entries [check]
Hint: Check-then-act gap -> duplicate add without sync [check]
Common Mistakes:
5. In a parking lot system, if parkVehicle() is not synchronized, which exact thread interleaving causes double-booking of the same spot?
medium
A. Thread 1 parks vehicle, Thread 2 parks vehicle simultaneously without checking availability.
B. Thread 1 updates spot status after parking, Thread 2 reads stale status and parks again.
C. Thread 1 releases spot, Thread 2 immediately parks without synchronization.
D. Thread 1 checks spot availability -> true, context switch; Thread 2 checks availability -> true; both park concurrently.

Solution

  1. Step 1: Identify the check-then-act race condition

    Without synchronization, Thread 1 reads spot as available and is preempted before marking it occupied. Thread 2 also reads available and proceeds to park.
  2. Step 2: Confirm both threads park concurrently causing double-booking

    Both threads believe the spot is free and park, leading to inconsistent state and double-booking.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Unsynchronized check-then-act -> double-booking race [check]
Hint: Check-then-act without sync -> double-booking race condition [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