점층적 생성자 패턴
•
정적 팩터리와 생성자의 경우에는 선택적 매개변수가 많은 경우 적절히 대응하기 어렵다.
•
일반적으로 점층적 생성자 패턴(telescoping constructor pattern)이 주로 사용되는데, 선택적 매개변수가 많다면 아래처럼 매개변수를 1개 받는 생성자, 2개 받는 생성자, 3개 받는 생성자… 형태로 생성자나 정적 팩터리를 많이 만들어야 한다.
public 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, serving, 0);
}
public NutritionFacts(int servingSize, int servings, int calories) {
this(servingSize, serving, calories, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat) {
this(servingSize, serving, calories, fat, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
this(servingSize, serving, calories, fat, sodium, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
...
}
Java
복사
•
만약 위에서 매개변수가 더 늘어난다면 코드의 길이가 너무 길어지고 가독성이 나빠지게 된다.
•
이런 점층적 생성자 패턴의 경우는 실수로 매개변수 순서를 바꾸어 호출하더라도 오류가 발생하는게 아니라 런타임 중에 오동작을 하게 된다.
자바빈즈 패턴
•
선택 매개변수가 많은 경우 사용할 수 있는 패턴 중 하나인 자바빈즈 패턴(JavaBeans pattern)이 있다.
•
자바빈즈 패턴은 매개변수가 없는 생성자로 객체를 생성 후 지정자(setter)를 통해 원하는 매개변수의 값들만 선택적으로 변경하는 방식이다.
public class NutritionFacts {
private final int servingSize = -1;
private final int servings = -1;
private final int calories = 0;
private final int fat = 0;
private final int sodium = 0;
private final int carbohydrate = 0;
public NutritionFacts() {}
public void setServingSize(int val) { servingSize = val; }
public void setServing(int val) { serving = val; }
public void setCalories(int val) { calories = val; }
public void setFat(int val) { fat = val; }
public void setSodium(int val) { sodium = val; }
public void setCarbohydrate(int val) { carbohydrate = val; }
}
Java
복사
•
자바빈즈 패턴을 사용하면 점층적 생성자 패턴의 단점들은 해결이 되지만, 객체 하나를 생성할 때 여러 지정자(setter) 메서드를 호출해야하고 객체가 완전히 생성되기 전에는 일관성(consistency)이 무너진 상태에 놓이게 된다.
•
또한 자바빈즈 패턴을 적용한 클래스는 지정자 때문에 클래스를 불변으로 만들 수 없고, 스레드 안정성을 얻으려면 별도의 동기화 작업이 필요하다.
빌더 패턴
•
점층적 생성자 패턴의 안전성과 자바빈즈 패턴의 가독성을 겸비한 빌더 패턴(Builder pattern)을 사용하자.
•
필요한 객체를 직접 만드는 대신, 필수 매개변수만을 받는 생성자에서 빌더 객체를 얻어 빌더 객체가 제공하는 일종의 지정자(sette)로 선택 매개변수들을 적용하는 방법이다.
public 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 static class Builder {
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 Builder(int servingSize, int servings) {
this(servingSize, serving, 0);
}
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) {
calories = 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;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
Java
복사
•
빌더 패턴을 사용하면 불변 클래스로 만들 수 있고, 빌더는 자기 자신을 반환하기 때문에 연쇄적으로 호출할 수 있다. 이런 방식을 fluent API 혹은 메서드 연쇄(method chaining)이라 한다.
•
빌더 패턴은 사용하기 쉽고, 읽기에도 좋다. 불변식을 보장하려면 빌더가 호출하는 생성자에서 여러 매개변수의 유효성 검사와 매개변수를 복사하여 저장하자.
•
이런 빌더 패턴은 계층적으로 설계된 클래스와 함께 사용하기에 좋다. 추상 클래스를 상속받는 여러 클래스들에서 각기 다른 필드를 사용한다면, 그에 맞춰 빌더를 작성하면 각 클래스마다 필요한 인수들만 사용할 수 있다.
•
빌더 패턴은 가변인수를 적용할 수 있기도 하고, 상단히 유연하게 사용할 수 있다.
•
다만 빌더를 만들어주어야 하고, 빌더로 인한 약간의 성능 저하가 발생할 수 있다. 또한 점층적 생성자 패턴보다는 코드가 복잡한 구조를 가지고 있어 매개변수가 4개보다 많을 때 사용해야 빛을 발한다.