Sobes.tech
Middle

What is the advantage of the Builder design pattern compared to a regular constructor?

sobes.tech AI

Answer from AI

The Builder pattern offers the following advantages over a regular constructor:

  • Improved readability and flexibility: When there are many optional parameters, a constructor can become bulky and hard to read. Builder allows constructing an object step-by-step, assigning values to fields using methods named according to the fields' purpose.
  • Construction safety: Unlike a constructor with many parameters, where it's easy to confuse the order of arguments, in Builder each method is responsible for setting a specific field.
  • Support for changing requirements: Adding new optional parameters in the future does not break existing code that uses Builder. For a constructor, new overloaded constructors would need to be created or the existing one changed, which could lead to compatibility issues.
  • Fewer constructors: Builder eliminates the need for multiple overloaded constructors required to handle different parameter combinations.

Example of using Builder:

// Class we want to build
public class Meal {
    private final String mainCourse;
    private final String sideDish;
    private final String beverage;
    private final String dessert;

    private Meal(Builder builder) {
        this.mainCourse = builder.mainCourse;
        this.sideDish = builder.sideDish;
        this.beverage = builder.beverage;
        this.dessert = builder.dessert;
    }

    // Static nested Builder class
    public static class Builder {
        private String mainCourse;
        private String sideDish;
        private String beverage;
        private String dessert;

        public Builder mainCourse(String mainCourse) {
            this.mainCourse = mainCourse;
            return this; // Return Builder for chaining
        }

        public Builder sideDish(String sideDish) {
            this.sideDish = sideDish;
            return this;
        }

        public Builder beverage(String beverage) {
            this.beverage = beverage;
            return this;
        }

        public Builder dessert(String dessert) {
            this.dessert = dessert;
            return this;
        }

        public Meal build() {
            return new Meal(this); // Create Meal object
        }
    }

    // Getters for fields (or other methods)
    public String getMainCourse() { return mainCourse; }
    public String getSideDish() { return sideDish; }
    public String getBeverage() { return beverage; }
    public String getDessert() { return dessert; }

    @Override
    public String toString() {
        return "Meal{" +
               "mainCourse='" + mainCourse + '\'' +
               ", sideDish='" + sideDish + '\'' +
               ", beverage='" + beverage + '\'' +
               ", dessert='" + dessert + '\'' +
               '}';
    }
}

// Using Builder
public class Main {
    public static void main(String[] args) {
        Meal completeMeal = new Meal.Builder()
            .mainCourse("Steak")
            .sideDish("Potatoes")
            .beverage("Water")
            .dessert("Cheesecake")
            .build();

        Meal simpleMeal = new Meal.Builder()
            .mainCourse("Chicken")
            .build(); // Can be assembled with incomplete set of fields

        System.out.println(completeMeal);
        System.out.println(simpleMeal);
    }
}