정적 팩터리와 생성자는 선택적 매개변수가 많을 때 적절히 대응하기 어렵다!
점층적 생성자 패턴을 많이 사용하나
매개변수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다.
public class NutritionFacts1 {
private final int servingSize; // (ml, 1회 제공량) 필수
private final int servings; // (회, 총 n회 제공량) 필수
private final int calories; // (1회 제공량당) 선택
private final int fat; // (g/1회 제공량) 선택
private final int sodium; // (mg/1회 제공량) 선택
private final int carbonhydrate; // (g/1회 제공량) 선택
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, sodium, 0);
}
public NutritionFacts1(int servingSize, int servings, int calories, int fat, int sodium, int carbonhydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbonhydrate = carbonhydrate;
}
}
두번째 대안은 자바빈즈 패턴!!
매개변수가 없는 생성자로 객체를 만든 후, 세터 메서드들을 호출해 원하는 매개변수의 값을 설정하는 방식
// 자바빈즈 패턴 - 일관성이 깨지고 불변으로 만들 수 없다.
public class NutritionFacts2 {
// 매개변수들을 (기본값이 있다면) 기본값으로 초기화된다.
private int servingSize = -1; // 필수: 기본값 없음
private int servings = -1; // 필수: 기본값 없음
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbonhydrate = 0;
public NutritionFacts2() {
}
// setters
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 setCarbonhydrate(int carbonhydrate) {
this.carbonhydrate = carbonhydrate;
}
public static void main(String[] args) {
NutritionFacts2 cocaCola = new NutritionFacts2();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbonhydrate(27);
}
}
바로 바로 바로 점층적 생성자 패턴의 안전써엉!!과 자바빈즈 패턴의 가독성까지!!! 겸비한 인재 빌더 패턴이시다.
클라이언트는 필요한 객체를 직접 만드는 대신, 필수 매개변수만으로 생성자(혹은 정적 팩터리)를 호출해 빌더 객체를 얻는다.
public class NutritionFacts3 {
private final int servingSize; // (ml, 1회 제공량) 필수
private final int servings; // (회, 총 n회 제공량) 필수
private final int calories; // (1회 제공량당) 선택
private final int fat; // (g/1회 제공량) 선택
private final int sodium; // (mg/1회 제공량) 선택
private final int carbonhydrate; // (g/1회 제공량) 선택
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 carbonhydrate = 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 carbonhydrate(int val) {
carbonhydrate = val;
return this;
}
public NutritionFacts3 build() {
return new NutritionFacts3(this);
}
}
public NutritionFacts3(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbonhydrate = builder.carbonhydrate;
}
public static void main(String[] args) {
NutritionFacts3 cocaCola = new NutritionFacts3.Builder(240, 8)
.calories(100).sodium(35).carbonhydrate(27).build();
}
}
계층적으로 설계된 클래스와 잘 어울리는 빌더 패턴
코드 작성 예정
핵심정리
생성자나 정적 팩터리가 처리해야 할 매개변수가 많다면 빌더 패턴을 선택하는 게 더 낫다.
매개변수 중 다수가 필수가 아니거나 같은 타입이면 특히 더 그렇다. 빌더는 점층적 생성자보다 클라이언트 코드를 읽고
쓰기가 훨씬 간결하고, 자바빈즈보다 훨씬 안전하다.