[Effective Java] 규칙2: 생성자 인자가 많다면 Builder 패턴 고려해보기
생성자나 정적 팩터리 메서드 둘 다 선택적 파라미터가 많은 경우에 문제가 있다.
1. 점층적 생성자 패턴
영양 성분표를 나타내는 클래스를 예로 들어보자.
public class NutritionFacts { private final int servingSize; // 필수로 입력되어야하는 파라미터 private final int servings; // 필수 private final int calories; // 선택 private final int fat; // 선택 private final int protein; // 선택 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); } // 위의 패턴대로 파라미터 갯수에 따라 모든 생성자를 만들어야 한다. }
위와 같은 패턴을 점층적 생성자 패턴이라 하는데, 파라미터 갯수별로 생성자를 모두 만들어야 한다. 당연히 코드는 잘 동작할테지만 파라미터가 늘어나면 늘어날수록 가독성이 떨어지게 된다.
2. 자바빈 패턴
생성자에 전달되는 파라미터 갯수가 많을 때 적용 가능한 두 번째 대안은 자바빈 패턴이다.
public class NutritionFacts { private final int servingSize; // 필수로 입력되어야하는 파라미터 private final int servings; // 필수 private final int calories; // 선택 private final int fat; // 선택 private final int protein; // 선택 private final int carbohydrate // 선택 public NutritionFacts() {} public void setServingSize(int val) { servingSize = val; } public void setServings(int val) { servings = val; } public void setCalories(int val) { calories = val; } public void setFat(int val) { fat = val; } public void setProtein(int val) { protein = val; } public void setCarbohydrate(int val) { carbohydrate = val; } }
이 패턴은 점층적 생성자 패턴보다 객체를 생성하기도 쉽고, 가독성도 좋다. 하지만, 자바빈 패턴에는 심각한 단점이 있는데, 1회의 함수 호출로 객체 생성을 끝낼 수 없으므로 객체 일관성이 일시적으로 깨질 수 있다는 것이다. 생성자의 인자가 유효한지 검사하여 일관성을 보장하는 단순한 방법을 여기서는 사용할 수 없다. 이와 관련된 또 다른 문제는 자바빈 패턴으로는 변경 불가능(immutable) 클래스를 만들 수 없다는 것이다. 스레드 안전성(thread-safety)을 제공하기 위해 해야할 일도 더 많아진다.
3. 빌더 패턴
Builder
라는 내부 객체를 이용하는 방법으로 코드를 살펴보자.
public class NutritionFacts { private final int servingSize; // 필수로 입력되어야하는 파라미터 private final int servings; // 필수 private final int calories; // 선택 private final int fat; // 선택 private final int protein; // 선택 private final int carbohydrate // 선택 public static class Builder { private final int servingSize; // 필수로 입력되어야하는 파라미터 private final int servings; // 필수 private final int calories = 0; // 선택 private final int fat = 0; // 선택 private final int protein = 0; // 선택 private final 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 protein(int val) { protein = val; return this; } public Builder carbohydrate(int val) { carbohydrate = val; return this; } public NutritionFacts build() { return new NutritionFacts(this); } } private NutritionFacts(Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; protein = builder.protein; carbohydrate = builder.carbohydrate; } }
이어서 객체를 생성하는 코드를 살펴보자.
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8) .calories(100) .fat(10) .carbohydrate(27) .build();
위와 같은 방법은 작성하기도 쉽고 가독성도 좋다.
'Programming > Effective Java' 카테고리의 다른 글
[Effective Java] 규칙1: 생성자 대신 정적 팩터리 메서드 고려해보기 (0) | 2020.07.29 |
---|
댓글을 사용할 수 없습니다.