ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아이템2. 생성자에 매개변수가 많다면 빌더를 고려하라
    책/이펙티브자바 2021. 6. 20. 13:53

    정적팩토리나 생성자에는 똑같은 제약이 있다.

    → 둘 다 선택적 매개변수가 많을때 적절히 대응이 어렵다는 점

     

    이렇게 선택적 매개변수가 많을때는 어떻게 효율적으로 사용할 수 있을까 ?

    • 점층적 생성자 패턴
    • 자바빈즈 패턴
    • 빌더 패턴

    점층적 생성자 패턴 (telescoping constructor pattern)

    필수 매개변수만 받는 생성자 → 필수 매개변수, 선택 매개변수1 ... 필수 매개변수, 선택 매개변수 n ... 이렇게 전부 다 받는 생성자까지 늘려가는 방식

    → 쓸 수는 있지만, 매개변수 개수가 많아지면 클라이언트 코드 작성이나 읽기가 어렵다.

    → 각 값의 의미가 헷갈림.

    → 타입이 같은 매개변수가 연달아 있으면 찾기 어려움.

    → 결국 런타임에 엉뚱한 동작 발생.

     

    자바빈즈 패턴 (JavaBeans pattern)

    매개변수가 없는 생성자로 객체를 만든 후, setter 메서드를 호출해 원하는 매개변수의 값을 설정하는 방식

    → 코드가 길어지지만 인스턴스 생성이 쉬움.

    → 코드 가독성이 좋음.

    → 객체 하나를 만들기 위해선 setter 메소드 호출이 너무 잦을 수 있음.

    → 객체가 완전히 생성되기 까지는 일관성 (consistency)가 무너진 상태임.

    → 클래스를 불변으로 만들수 없음.

    → 스레드 안전성을 위해서는 추가작업이 필요함.

     

    빌더 패턴 (Builder pattern)

    점층적 생성자 패턴의 안전성 + 자바빈즈 패턴의 가독성 ⇒ 빌더 패턴

    클라이언트는 필요한 객체를 직접 만드는 대신, 필수 매개변수만으로 생성자(혹은 정적 팩토리)를 호출해 빌더 객체를 얻는다.

    → 쓰기 쉽고, 읽기 쉬움.

    → 유연하다. (빌더 하나로 여러 객체를 순회하면서 만들 수 있고, 빌더에 넘기는 매개변수에 따라 다른 객체를 만들 수 도 있다.)

    → 코드가 장황해 매개변수가 4개 이상은 되어야 값어치를 한다. (API 는 시간이 지날수록 매개변수가 많아지는 경향이 있다.)

    → 객체를 만들기 이전에 빌더 부터 만들어야 한다. (빌더 생성 비용이 크지는 않지만 성능에 민감한 상황에서는 문제가 될 수 있음.)

    → 생성자나 정적팩토리 방식으로 시작했다가 추후에 매개변수가 많아지면 빌더 패턴으로 바꿀수도 있지만, 애초에 빌더로 시작하는 편이 권장된다.

    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 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 NutritionFacts(Builder builder) {
            servingSize = builder.servingSize;
            servings = builder.servings;
            calories = builder.calories;
            fat = builder.fat;
            sodium = builder.sodium;
            carbohydrate = builder.carbohydrate;
        }
    }
    
    // 클라이언트 코드
    NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
                    .calories(100)
                    .sodium(35)
                    .carbohydrate(27)
                    .build();
킹수빈닷컴