정적 팩토리와 생성자에는 똑같은 제약이 있다. 선택적 매개변수가 많을 때 적절히 대응하기 어렵다는 점이다.
1. 점층적 생성자 패턴
필수 매개변수만 받는 생성자, 필수 매개변수와 선택 매개변수 1개를 받는 생성자, 선택 매개변수를 2개까지 받는 생성자, ... 형태로 선택 매개변수를 전부 다 받는 생성자까지 늘려가는 방식이다.
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, 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);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
this(servingSize, servings, calories, fat, sodium, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
}
public class Main {
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);
}
}
단점
- 매개변수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다.
2. 자바빈즈 패턴
매개변수가 없는 생성자로 객체를 만든 후, setter 메서드를 호출해 원하는 매개변수 값을 설정하는 방식이다.
public class NutritionFacts {
private int servingSize = -1;
private int servings = -1;
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts() {}
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 setCarbohydrate(int carbohydrate) {
this.carbohydrate = carbohydrate;
}
}
public class Main {
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setFat(0);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);
}
}
단점
- 객체 하나를 만들려면 메서드를 여러 개 호출해야 한다.
- 객체가 완성되기 전까진 일관성이 무너진 상태에 놓이게 된다.
-> 일관성이 무너지는 문제 때문에 클래스를 불변으로 만들 수 없으며 이를 위해 수동으로 freeze 작업을 해야한다.
3. 빌더 패턴
필요한 객체를 직접 만드는 대신, 필수 매개변수만으로 생성자(혹은 정적 팩토리)를 호출해 빌드 객체를 얻은 후, 빌더 객체가 제공하는 일종의 세터 메서드들로 원하는 선택 매개변수를 설정한다. 마지막으로 매개변수가 없는 build 메서드를 호출해 필요한 객체를 얻는 방식이다.
점층적 생성자 패턴의 안전성과 자바 빈즈 패턴의 가독성을 겸비한 패턴이다.
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 carbohydrates;
public static class Builder {
// 필수 매개변수
private final int servingSize;
private final int servings;
// 선택 매개변수
private int calories;
private int fat;
private int sodium;
private int carbohydrates;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int calories) {
this.calories = calories;
return this;
}
public Builder fat(int fat) {
this.fat = fat;
return this;
}
public Builder sodium(int sodium) {
this.sodium = sodium;
return this;
}
public Builder carbohydrates(int carbohydrates) {
this.carbohydrates = carbohydrates;
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;
carbohydrates = builder.carbohydrates;
}
}
NutritionFacts 클래스는 불변이며, 모든 매개변수의 기본값들을 한곳에 모아뒀다.
빌더의 세터 메서드들은 빌더 자신을 반환하기 때문에 연속해서 호출할 수 있다.
결론
생성자나 정적 팩터리가 처리해야 할 매개변수가 많다면 빌더 패턴을 선택하는 게 더 낫다.
매개변수 중 다수가 필수가 아니거나 같은 타입이면 특히 더 그렇다.
빌더는 점층적 생성자보다 클라이언트 코드를 읽고 쓰기가 훨씬 더 간결하고, 자바빈즈 보다 훨씬 안전하다.
자바빈즈 패턴은 객체를 생성한 후, 값을 setter 메서드를 통해 넣는다.
setter 메서드를 통해 유효하지 않은 값이나 null값이 들어갈 수 있다.
반면, 빌더 패턴은 객체 생성 전, 값을 setter 메서드를 통해 넣는다.
마지막으로 build 메서드를 호출하여 객체를 생성한다.
그렇기 때문에 객체 사용 중에 값이 변경될 우려가 없으며, 불변성과 안정성이 올라간다.
'자바 > 이펙티브자바' 카테고리의 다른 글
[아이템 5] 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2022.03.16 |
---|---|
[아이템 1] 생성자 대신 정적 팩토리 메소드를 고려하라. (0) | 2021.10.14 |