Builder Pattern

  • separates the construction of a complex object from its representation

  • allows you to create different types and representations of an object using the same construction process.

  • Formal definition:

    • Builder pattern builds a complex object step by step. It separates the construction of a complex object from its representation, so that the same construction process can create different representations.

  • In-simpler terms:

    • Imagine you're ordering a custom burger. You choose the bun, patty, toppings, sauces, and whether you want it grilled or toasted.

    • The chef follows your instructions step by step to build your custom burger.

    • This is what the Builder Pattern does — it lets you construct complex objects by specifying their parts one at a time, giving you flexibility and control over the object creation process.

  • Real life analogy (custom pizza order)

    • Think of ordering a pizza online. You select the crust type, size, toppings, cheese, and sauce — all step by step.

    • The pizza shop then builds your pizza according to your selections.

    • Different customers can use the same process to get entirely different pizzas.

    • This is the essence of the Builder Pattern: a structured, step-wise approach to creating customised complex objects.

  • Understanding the problem

    • Imagine you're building a BurgerMeal in your application.

    • A burger must have some mandatory components like: Bun and Patty.

    • And it can also include option components like: Sides, Toppings, and Cheese.

  • Issues in Code: This constructor approach works, but it creates multiple problems:

    • Hard to Read and Maintain: The user has to remember the order of parameters and their types. It becomes difficult to read when more optional parameters are added.

    • Unnecessary null values: Even if the user doesn’t want toppings or sides, they still have to pass null explicitly. This clutters the object creation code.

    • Risk of NullPointerException: If we forget to null-check before accessing optional values inside the class, it may lead to runtime exceptions.

    • Too Many Constructor Overloads: To handle various combinations (e.g., with cheese, without sides, only toppings, etc.), you’d need to create multiple overloaded constructors — which is not scalable.

    • Tight Coupling Between Parameters and Construction: There is no flexibility to set values step by step. The entire object must be built in one go, which doesn't match the natural way of ordering or customising a burger.

  • Telescoping Constructor Anti-Pattern

    • To manage optional parameters, many developers try to solve this by writing multiple overloaded constructors — each with one more optional parameter than the last.

    • For example:

  • This is called Telescoping Constructor Anti-Pattern.

  • But this creates a cascade of constructors that become:

    • Hard to read and write

    • Error-prone due to confusing parameter order

    • Difficult to maintain when more fields are added

    • Inflexible, as users must use parameters in a specific order

  • It occurs most commonly in Java, which lacks support for optional or default parameters (unlike C++ or Python).

  • Because of this limitation, developers are forced to create multiple constructor overloads to handle different combinations of parameters.

  • Clearly, this approach doesn't scale well. And this is exactly the kind of problem that the Builder Pattern is designed to solve.

  • It gives the user full control over which parts to build while keeping the construction code clean, readable, and safe.

  • The Solution

    • To solve the problems we saw earlier with constructors, we use the Builder Pattern.

    • It separates object construction from its representation, allowing us to build step-by-step while keeping the object immutable and readable.

  • Understanding the Code

    • Private Constructor The constructor of BurgerMeal is made private so that object creation is restricted to the Builder only.

    • Nested Static BurgerBuilder Class This builder class holds the same fields as BurgerMeal. It ensures immutability and keeps construction controlled.

    • Fluent API Style Each method (like withCheese, withSide) returns the builder itself, enabling method chaining in a fluent and readable manner.

    • Selective Configuration Only required fields (bunType, patty) are passed to the builder's constructor. Everything else is optional and set via withXYZ() methods.

    • Final Step: build() Once all desired fields are set, calling .build() finalizes the object construction and returns the BurgerMeal instance.

  • Why This is Better

    Aspect
    Constructor Approach
    Builder Pattern

    Object readability

    Poor (nulls, long argument list)

    Excellent (fluent and expressive)

    Flexibility

    Low (all-or-nothing setup)

    High (configure only what’s needed)

    Maintainability

    Hard to scale with more fields

    Easy to extend with more options

    Safety

    High chance of errors with nulls

    Controlled and safe instantiation

  • When to Use and When to Avoid the Builder Pattern

    When to Use?

    You should consider using the Builder Pattern in the following scenarios:

    • An object has multiple fields, especially when many of them are optional. Managing such objects using constructors becomes messy and error-prone.

    • Immutability is preferred - Builder lets you construct an object step by step and then make it immutable once built.

    • You want readable, maintainable object creation, especially when dealing with domain models or configuration objects. The fluent interface style improves clarity and flexibility.

    When to Avoid?

    The Builder Pattern can be overkill in simpler use cases. Avoid it when:

    • Your class has only 1-2 fields: Using a constructor or setter methods is simpler and more concise.

    • You don’t need object customization or immutability: If the object is small, mutable, or built only in one place, a builder adds unnecessary complexity.

  • Pros and Cons of Builder Pattern

    Understanding both the advantages and limitations of the Builder Pattern helps in deciding when to use it effectively.

    Pros

    • Avoids constructor telescoping: You no longer need to write multiple overloaded constructors for different configurations.

    • Ensures immutability: The final object can be made immutable once built, which improves safety and thread-safety.

    • Clean, readable object creation: The fluent API makes object construction expressive and easy to follow.

    • Great for complex configurations: If your object has many optional parameters or conditional setup, the builder pattern keeps it organized.

    Cons

    • Slightly tough to set up: Initial setup requires writing a separate builder class, which adds to boilerplate.

    • Overkill for small classes: If a class only has one or two fields, using a builder adds unnecessary complexity.

    • Separate builder class needed: You need to maintain a second class or static inner class just to construct the main object, increasing maintenance.

  • Real World Products Using Builder Pattern

    - Amazon Cart configuration

    • Think about Amazon's shopping cart system. When you add an item to your cart, you're not just storing an item ID. You're building a complex object with fields like:

      • Quantity

      • Size or color (for apparel)

      • Delivery option

      • Gift wrap

      • Save for later status

      • Discounted price or offer tag

      Each user may customize these options differently. Internally, such cart items are likely created using a Builder Pattern to allow step-by-step configuration while ensuring data consistency and immutability.

Last updated