ch1.객체의 생성과 파괴

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

선택 매개변수가 많을때 사용할 수 있는 방법

점층적 생성자 패턴

필수 매개변수만 받는 생성자, 필수와 선택 1개 받는 생성자, 필수와 선택 2개 받는 생성자… 이렇게 점점 매개변수 개수를 늘려가는 방식입니다. 점층적 생성자 패턴도 방법이지만 매개변수가 많아지면 코드를 읽고 작성하는데 어렵습니다. 각 매개변수의 의미가 무엇인지 헷갈리고 개수도 헷갈립니다. 안정적이지만 가독성이 없습니다.

public class TelescopingConstructor {
    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);
    }
}

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

}

자바빈즈 패턴

자바빈즈 패턴은 setter를 이용하여 객체를 만드는 방법입니다. 객체가 완전히 생성되기 전까지는 일관성이 무너진 상태입니다. 일관성이 깨진 객체가 만들어지면 버그 코드와 런타임에 문제를 겪는 코드가 물리적으로 떨어져있을것이기 떄문에 디버깅에도 어려움을 겪습니다. 가독성은 좋지만 안정성이 없습니다.

public class JavaBeans {
    public static void main(String[] args) {
        NutritionFacts nutritionFacts = new NutritionFacts();
        nutritionFacts.setServingSize(240);
        nutritionFacts.setServings(8);
        nutritionFacts.setCalories(100);
        nutritionFacts.setSodium(35);
        nutritionFacts.setCarbohydrate(27);
    }

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

}

빌더 패턴

NutritionFacts클래스는 불변이고 매개변수의 기본값들을 Builder클래스에 모아두어서 가독성이 좋습니다. 빌더패턴은 메소드 호출이 연결된다는 뜻으로 fluent API, method chaining이라고도 합니다.

public class Builder {
    public static void main(String[] args) {
        NutritionFacts nutritionFacts = new NutritionFacts.Builder(240, 8)
                .calories(100)
                .sodium(35)
                .carbohydrate(27)
                .build();
    }
}

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

    public static class Builder{
        // Required parameters
        private final int servingSize;
        private final int servings;
        
        // Optional parameters - initialized to default values
        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 class PizzaShop {
  public static void main(String[] args) {
    NewYorkPizza nyPizza = new NewYorkPizza.Builder(NewYorkPizza.Size.SMALL)
      .addTopping(Pizza.Topping.SAUSAGE)
      .addTopping(Pizza.Topping.ONION)
      .build();

    CalzonePizza calzonePizza = new CalzonePizza.Builder()
      .addTopping(Pizza.Topping.HAM)
      .sauceInside()
      .build();
  }
}
public abstract class Pizza {
    public enum Topping {
        HAM, MUSHROOM, ONION, PEPPER, SAUSAGE
    }
    final Set<Topping> toppings;

    abstract static class Builder<T extends Builder<T>>{
        EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);

        public T addTopping(Topping topping) {
            toppings.add(Objects.requireNonNull(topping));
            return self();
        }

        abstract Pizza build();

        // Subclasses must override this method to return "this"
        protected abstract T self();
    }

    Pizza(Builder<?> builder){
        toppings = builder.toppings.clone();
    }

}
public class NewYorkPizza extends Pizza{
    public enum Size {SMALL, MEDIUM, LARGE}
    private final Size size;

    public static class Builder extends Pizza.Builder<Builder>{
        private final Size size;

        public Builder(Size size){
            this.size = Objects.requireNonNull(size);
        }

        @Override
        public NewYorkPizza build() {
            return new NewYorkPizza(this);
        }

        @Override
        protected Builder self() {
            return this;
        }
    }

    NewYorkPizza(Builder builder) {
        super(builder);
        size = builder.size;
    }
}
public class CalzonePizza extends Pizza{

    private final boolean sauceInside;

    public static class Builder extends Pizza.Builder<Builder>{
        private boolean sauceInside = false;

        public Builder sauceInside() {
            sauceInside = true;
            return this;
        }

        @Override
        CalzonePizza build() {
            return new CalzonePizza(this);
        }

        @Override
        protected Builder self() {
            return this;
        }
    }

    private CalzonePizza(Builder builder) {
        super(builder);
        sauceInside = builder.sauceInside;
    }
}

댓글남기기