[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 |
---|