[Java] [Read Effective Java] Chapter 2, Item 2 Consider a builder when faced with many constructor parameters

2 minute read

Consider a builder when faced with many constructor parameters

When creating a class that needs to pass a lot of parameters when doing new Talk that it is good for readability and safety if you use “builder”

Sample code

Nutrition Facts is a class that represents nutritional ingredients For example, consider a case where you have a lot of fields such as a serving size, amount per container, serving calories, etc. ・Telescoping constructor pattern If there are many parameters, it becomes difficult to understand what should be passed to the argument when new is done, and the readability is also disadvantageous.

・JavaBeans pattern Readability is improved unlike the telescoping constructor pattern, but an inconsistent state may occur while setting parameters.

・Builder pattern The best of telescoping constructors and JavaBeans!

Example 1


// Telescoping constructor pattern
public class NutritionFacts{
    private final int servingSize; //(mL) Required
    private final int servings; // (per container) required
    private final int calories; // optional
    private final int fat; //(g) option
    private final int sodium; //(mg) option
    private final int carbohydrate; //(g) option

    public NutritionFacts(int servingSize, int servings){
        this(servingSize, servings, 0, 0, 0, 0);
    }

    public NutritionFacts(int servingSize, int servings,
             int calories ){
        this(servingSize, servings, calories, 0, 0, 0);
    }
    
    public NutritionFacts(int servingSize, int servings,
            int calories, int fat ){
        this(servingSize, servings, calories, fat, 0, 0);
    }

    public NutritionFacts(int servingSize, int servings,
            int calories, int fat, int sodium ){
        this(servingSize, servings, calories, fat, sodium, 0);
    }

    public NutritionFacts(int servingSize, int servings,
            int calories, int fat, int sodium, carbohydrate ){
        this.servingSize = servingSize;
        this.servings = servings;
        this.calories = calories;
        this.fat = fat;
        this.sodium = sodium;
        this.carbohydrate = carbohydrate;
    }
}

It's hard to see when doing //new
NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);

Example 2


//JavaBeans pattern
public class NutritionFacts{
    private int servingSize = -1; //(mL) Required
    private int servings = -1; // (per container) required
    private int calories = 0; //
    private int fat = 0; //(g)
    private int sodium = 0; //(mg)
    private int carbohydrate = 0; //(g)

    public NutritionFacts(){}

    // setter
    public void setServingSize(int val) {servingSize = val;}
    public void setServings(int val) {servings = val;}
    public void setCalories(int val) {calories = val;}
    public void setFat(int val) {fat = val;}
    public void setSodium(int val) {sodium = val;}
    public void setCarbohydrate(int val) {carbohydrate = val;}
}

// When doing new, it is easy to see, but if it is accessed while setting it is a bit troublesome
NutritionFact cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);

Example 3


// Builder pattern
public class NutritionFacts{
    private final int servingSize; //(mL)
    private final int servings; // (per container)
    private final int calories; //
    private final int fat; //(g)
    private final int sodium; //(mg)
    private final int carbohydrate; //(g)

    public static class Builder{
        // Required parameter
        private final int servingSize; //(mL)
        private final int servings; // (per container)
        
        // optional parameter
        private int calories = 0; //
        private int fat = 0; //(g)
        private int sodium = 0; //(mg)
        private int carbohydrate = 0; //(g)

        public Builder(int servingSize, int servings){
            this.servingSize = servingSize;
            this.servings = servings;
       }

       public Builder calories(int val){
            calories = val;
            return this;
       }

       public Builder fat(int val){
            fat = val;
            return this;
       }

       public Builder sodium(int val){
            sodium = val;
            return this;
       }

       public Builder carbohydrate(int val){
            carbohydrate = val;
            return this;
       }

       public NutritionFacts build() {
           return new NutritionFacts(this);
       }
    }

    private NutritionFacts(Builder builder){
         servingSize = builder.servingSize;
         servings = builder.servings;
         calories = builder.calories;
         fat = builder.fat;
         sodium = builder.sodium;
         carbohydrate = builder.carbohydrate;
    }
}

//new and easy to see and safe!
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).
    calories(100).sodium(35).carbohydrate(27).build();