반응형

2장 객체 생성과 파괴

 

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

 

선택 매개변수가 많은 경우 대안

  1. 점층적 생성자 패턴

    public class NutritionFacts {
        
        private final int servingSize;	// 필수
        private final int servings;		// 필수
        private final int calories;		// 선택
        private final int fat;			// 선택
        private final int sodium;		// 선택
        
        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 calroies, int fat) {
            this(servingSize, servings, calories, fat, 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;
        }
    }
    
    • 필수 매개변수만 받는 생성자를 만들고 선택 매개변수를 받는 생성자를 늘려가는 방식

    • 단점

      • 매개변수 개수가 많아지면서 클라이언트 코드를 작성하거나 읽기 어려움.
  2. 자바빈즈 패턴

    NutritionFacts cocaCola = new NutritionFacts();
    cocaCola.setServingSize(240);
    cocaCola.setServings(8);
    cocaCola.setCalories(100);
    cocaCola.setSodium(35);
    
    • 매개변수가 없는 생성자로 객체를 만든 후, setter 메서드를 호출하여 값 설정

    • 단점

      • 객체 하나를 만들려면 메서드를 여러 개 호출해야 하고, 객체가 완전히 생성되기 전까지 일관성이 무너진 상태
      • 클래스를 불변으로 만들 수 없음
  3. 빌더 패턴

    public class NutritionFacts {
        
        private final int servingSize;	// 필수
        private final int servings;		// 필수
        private final int calories;		// 선택
        private final int fat;			// 선택
        private final int sodium;		// 선택
        
        public static class Builder {
            
            private final int servingSize;	// 필수
            private final int servings;		// 필수
    
            private final int calories;		// 선택
            private final int fat;			// 선택
            private final int sodium;		// 선택
    
            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 NutritionFacts build() {
                return new NutritionFacts(this);
            }
        }
        
        private NutritionFacts(Builder builder) {
            this.servingSize 	= builder.servingSize;
            this.servings 		= builder.servings;
            this.calories		= builder.calories;
            this.fat 			= builder.fat;
            this.sodium			= builder.sodium;
        }
        
    }
    
    • 필수 매개변수만으로 빌더 객체를 생성하고, 선택 매개변수 값을 설정한 후 build 메서드 호출

    • 생성할 클래스 안에 정적 멤버 클래스로 만듦.

    • 메서드 연쇄 방식 : 클래스 자신을 반환하는 메서드를 연쇄적으로 호출

    • 장점

      • 읽고 쓰기 쉬움 ( named optional parameters 방식과 유사 )

      • 계층적으로 설계된 클래스와 함께 쓰기 좋음

        public abstract class Pizza {
            
            public enum Topping {
                HAM, MUSHROOM, ONION, PEPPER, SAUUSAGE
            }
            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();
                
                protected abstract T self();
            }
            
            Pizza(Builder<?> builder) {
                toppings = builder.toppings.clone();
            }
            
        }
        
        public calass NyPizza 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 NyPizza build() {
                    return new NyPizza(this);
                }
                
                @Override
                protected Builder self() {
                    return this;
                }
            }
            
            private NyPizza(Builder builder) {
                super(builder);
                size = builder.size;
            }
        }
        
        public class Calzone extends Pizza {
            
            private final boolean sauceInside;
            
            public static class Builder extends Pizza.Builder<Builder> {
                
                private boolean sauceInside = false;
                
                public Builder sauceInside() {
                    this.sauceInside = true;
                }
                
                @Override
                public Calzone build() {
                    return new Calzone(this);
                }
                
                @Override protected Builder self() {
                    return this;
                }
            }
            
            private Calzone(Builder builder) {
                super(builder);
                this.sauceInside = builder.sauceInside;
            }
        }
        
      • 가변인수 매개변수를 여러 개 사용 가능

    • 단점

      • 빌더 구현 => Lombok

 

정리

생성자나 정적 팩토리가 처리해야 할 매개변수가 많다면 빌더 패턴 선택

 

용어

공변 반환 타이핑 ( covariant return typing) : 하위 클래스의 메서드가 상위 클래스의 메서드가 정의한 반환 타입이 아닌 하위 타입을 반환하는 기능

반응형

'Java > OOP' 카테고리의 다른 글

[Effective Java] 2장. 아이템6  (0) 2022.07.18
[Effective Java] 2장. 아이템5  (0) 2022.07.17
[Effective Java] 2장. 아이템4  (0) 2022.07.16
[Effective Java] 2장. 아이템3  (0) 2022.07.16
[Effective Java] 2장. 아이템1  (0) 2022.07.14

+ Recent posts