이펙티브자바, 빌드패턴, 들어가며
객체를 생성하는 다양한 방법
점진적 생성자
- 점진적 생성자란 모든 필드를 인자로 가지는 생성자(일종의 @AllArgsConstructor)를 만든다. 그리고 필요에 따라 일부분의 인자만을 가지는 생성자를 만들고, 자신보다 더 많은 인자를 가진 생성자에 대하여 this()로 생성자를 호출한다. 작은 생성자 -> 중간 생성자 -> 전체 생성자를 호출하는 형태를 가진다. 이러한 형태를 점진적 생성자라 한다.
public class NutritionFacts {
// 필드 값은 final로 한다. 모든 값에 대하여 생성자 인자로 삽입한다.
private final int servingSize;
private final int servings;
private final int caloreis;
private final int fat;
private final int sodium;
private final int carbohydrate;
// 점진적 생성자
public NutritionFacts(int servingSize, int servings, int caloreis, int fat, int sodium, int carbohydrate) {
super();
this.servingSize = servingSize;
this.servings = servings;
this.caloreis = caloreis;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
// 점진적 생성자
public NutritionFacts(int fat, int sodium, int carbohydreate) {
this(0,0,0,fat,sodium,carbohydreate);
}
// 점진적 생성자
public NutritionFacts(int calories, int fat, int sodium, int carbohydreate) {
this(0,0,calories,fat,sodium,carbohydreate);
}
}
- 점진적 생성자의 장점은 final을 사용 가능하다. 생성자는 여러 번을 사용하지만 final로 필드를 사용할 수 있다.
- 단점은 인자의 위치가 변경되거나 어떤 필드에 대한 값인지 인지하기가 어렵다.
- 각 생성자가 어떤 의도인지를 파악하기 어렵다.
자바빈즈 패턴, 세터
- 생성자는 기본 생성자만 존재한다. 세터를 통해 필요한 값을 주입한다.
- 점진적 생성자와 달리 필드값이 매서드에 분명하기 드러나기 때문에 이해하기 쉽다.
- 단점은 객체 생성 후 주입하기 때문에 final을 사용할 수 없다. 특정 필드의 setter를 누락하여 발생한 문제를 예방하기가 어렵다.
@Setter
public class NutritionFacts2 {
private int servingSize;
private int servings;
private int caloreis;
private int fat;
private int sodium;
private int carbohydrate;
public NutritionFacts2() {
}
}
@Test
void test(){
NutritionFacts2 obj = new NutritionFacts2();
obj.setCaloreis(123);
obj.setFat(123);
}
빌드패턴
- 점진적 생성자와 자바빈즈 패턴의 장점을 수용한 형태
- 클래스 내부에 정적 클래스 Builder를 만든다.
- Builder 객체를 생성하고 stream 처럼 매서드를 체인처럼 엮어서 값을 삽입한다. 마지막에 build() 매서드를 호출할 때, 해당 객체의 필드값을 구현하려는 객체의 AllArgConstructor 에 한번에 삽입하는 형태이다.
- 이를 통해 setter와 유사하게 매서드의 이름이 명시되고 동시에 원하는 값만을 삽입할 수 있다. 마지막에 전체 생성자로 목표하는 객체를 만들기 때문에 필드를 final로 할 수 있다. 생성자 생성 타임에 데이터의 정합성 및 예외처리 가능해서 좋다.
@Getter
@ToString
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 int calories;
private int fat;
private int sodium;
private int carbohydrate;
public Builder(int servingSize, int servings){
this.servings = servings;
this.servingSize = servingSize;
}
public Builder calories(int val){
this.calories = val;
return this;
}
public Builder fat(int val){
this.fat = val;
return this;
}
public Builder sodium(int val){
this.sodium = val;
return this;
}
public Builder carbohydrate(int val){
this.carbohydrate = val;
return this;
}
public NutritionFacts build(){
return new NutritionFacts(this);
}
}
public NutritionFacts(Builder builder){
this.servingSize = builder.servingSize;
this.servings = builder.servings;
this.calories = builder.calories;
this.fat = builder.fat;
this.sodium = builder.sodium;
this.carbohydrate = builder.carbohydrate;
}
}
참고
- 소스코드
- https://github.com/WegraLee/effective-java-3e-source-code