JaPark Bug World

item2. 생성자에 매개변수가 많다면 빌더를 고려하라. 본문

개발새발/Effective Java

item2. 생성자에 매개변수가 많다면 빌더를 고려하라.

JAstory 2024. 3. 26. 17:43

* 해당 내용은 인프런 이펙티브 자바 완벽 공략 1부 백기선님의 영상을 기준으로 작성하였습니다. https://inf.run/fnnRy

 

백기선 이펙티브 자바 완벽 공략 1부 강의 - 인프런

자바 학습의 끝판왕 “이펙티브 자바”를 보다 쉽고 완벽하게 학습하세요. 이 강의를 수강하시면 여러분의 자바 프로그래밍 지식과 실력뿐 아니라 학습 능력 또한 향상 시킬 수 있습니다., MS 시

www.inflearn.com

 

생성자에 매개변수가 많다면 빌더를 고려하라.

  • 정적 팩터리와 생성자에 선택적 매개변수가 많을 때 고려할 수 있는 방안
    • 대안1: 점층적 생성자 패턴 또는 생성자 체이닝
      1. 매개변수가 늘어나면 클라이언트 코드 작성하거나 읽기 어렵다.

- 점층적 생성자

public class NutritionFacts {
    private final int servingSize;      // 필수
    private final int servings;         // 필수
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

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

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

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

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

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

 

- 생성자 체이닝

public class NutritionFacts {
    private final int servingSize;      // 필수
    private final int servings;         // 필수
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

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

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

    public NutritionFacts(int servingSize, int servings, int calories, int fat) {
        this(servingSize, servings, calories, fat, 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, int carbohydrate) {
        this.servingSize = servingSize;
        this.servings = servings;
        this.calories = calories;
        this.fat = fat;
        this.sodium = sodium;
        this.carbohydrate = 0;
    }
}

 

- 단점

public static void main(String[] args) {
        // 인스턴스를 만들 때 어떤 파라미터를 줘야 하는지 파악하기가 어렵다.
        NutritionFacts cola = new NutritionFacts()
    }

 

  • 대안2: 자바빈즈 패턴
    1. 완전한 객체를 만들려면 메서드를 여러번 호출해야 한다. (일관성이 무너진 상태가 될 수도 있다.)
    2. 클래스를 불변으로 만들 수 없다.
public class NutritionFacts {
    private int servingSize;      // 필수
    private int servings;         // 필수
    private int calories;
    private int fat;
    private int sodium;
    private int carbohydrate;

    public NutritionFacts() {}

    public void setServingSize(int servingSize) {
        this.servingSize = servingSize;
    }

    public void setServings(int servings) {
        this.servings = servings;
    }

    public void setCalories(int calories) {
        this.calories = calories;
    }

    public void setFat(int fat) {
        this.fat = fat;
    }

    public void setSodium(int sodium) {
        this.sodium = sodium;
    }

    public void setCarbohydrate(int carbohydrate) {
        this.carbohydrate = carbohydrate;
    }
}
public static void main(String[] args) {
        NutritionFacts cola = new NutritionFacts();
        cola.setServingSize(10);
        cola.setServings(10);
        cola.setCalories(10);
        cola.setFat(10);
        cola.setSodium(10);
        cola.setCarbohydrate(10);
    }

 

- 단점

    public static void main(String[] args) {
        // 필수값으로 생성이 안된 부분에 대한 안정성을 보장할 수 없다.
        NutritionFacts cola = new NutritionFacts();
        cola.setCalories(10);
        cola.setFat(10);
        cola.setSodium(10);
        cola.setCarbohydrate(10);
    }

 

 

  • Builder pattern 
public class NutritionFacts {
    private final int servingSize;      // 필수
    private final int servings;         // 필수
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

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

    public static class Builder {
        private final int servingSize;
        private final int servings;
        private int calories = 0;
        private int fat = 0;
        private int sodium = 0;
        private int carbohydrate = 0;

        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);
        }
    }
}
public static void main(String[] args) {
        NutritionFacts cola = new Builder(100, 10)
                .calories(100)
                .sodium(100)
                .carbohydrate(100)
                .build();
    }
public static void main(String[] args) {   
        // 필수값만 입력받아 빌더 생성 가능
        // 최종적으로 .build()가 호출되야 함.
        NutritionFacts cola = new Builder(100, 100).build();
    }

 

- 롬복을 이용하여 코드의 양을 줄일 수 있다.

import lombok.Builder;

@Builder
public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static void main(String[] args) {
        NutritionFacts nutritionFacts = new NutritionFactsBuilder()
                .servingSize(100)
                .servings(100)
                .build();
    }
}