본문 바로가기
Java/Effective Java

[아이템 2] 생성자에 매개변수가 많다면 빌더를 고려하라

by setung 2021. 9. 28.

책에서는 선택적 매개변수가 많은 클래스의 인스턴스를 생성하는데 점층적 생성자 패턴(telescoping constructor pattern)자바빈즈 패턴(JavaBeans pattern) 그리고 아이템 2의 주제인 빌더 패턴(Builder pattern)을 소개하고 있어 간단히 설명해 보겠다.

 

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

class NutritionFacts1 {

    // 필수
    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 NutritionFacts1(int servingSize, int servings) {
        this(servingSize, servings, 0);
    }

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

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

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

    public NutritionFacts1(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;
    }
}

NutritionFacts 클래스엔 필수 값인 servingSize, servings와 선택 값인 calories, fat, sodium, carbohydrate 변수가 있다.

필수 값을 받는 생성자부터 선택 값을 받은 생성자들을 점층적으로 만들어 가는 방식이다.

 

장점

  • 멀티쓰레드 환경에 안전하다.

문제점

  • 매개변수가 추가되면 생성자도 추가 및 수정되어야 한다.
  • 생성자 호출 시 매개변수의 갯수와 위치를 생각해야 한다.
  • ex) new NutrionFacts(10,10,3,5,1)  // 각각의 값들이 의미가 무엇일까? 생성자를 보며 직접 확인해야 한다.

 

2. 자바빈즈 패턴(JavaBeans pattern)

class NutritionFacts2 {
    private int servingSize;
    private int servings;
    private int calories;
    private int fat;
    private int sodium;
    private int carbohydrate;

    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;
    }
}

자바빈즈란 디폴트 생성자와 프로퍼티(getter, setter)가 있는 형태를 말한다. 

즉 인스턴스를 매개변수가 없는 생성자로 생성 후, set 메서드를 통해 매개변수 값을 설정하는 방식이다.

 

장점

  • 생성자를 복잡하게 만들 필요가 없다.
  • set 메서드를 통해 어떤 값을 설정하는지 직관적이다.

문제점

  • 클라이언트에서 인스턴스를 생성하려면 set 메서드를 여러 번 호출해야 한다.
  • set 메서드로 완전히 값을 설정되지 않은 인스턴스를 다른 스레드가 참조할 수 있어 버그를 유발할 수 있다.
  • set 함수 존재 자체로 불면 클래스가 되지 못한다.

 

3. 빌더 패턴(Builder pattern)

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) {
        this.servingSize = builder.servingSize;
        this.servings = builder.servings;
        this.calories = builder.calories;
        this.fat = builder.fat;
        this.sodium = builder.sodium;
        this.carbohydrate = builder.carbohydrate;
    }

}

장점

  • 멀티 쓰레드 환경에 안전하다.
  • 가독성이 좋다.

문제점

  • 생성 비용이 크진 않지만 성능에 민감한 상황에서 문제가 도리 수 있다. 
  • 매개변수가 적은 경우 빌더 패턴 사용 시 코드가 장황해질 수 있다.

댓글