아이템2. 생성자에 매개변수가 많다면 빌더를 고려하라.
선택적 매개변수가 많을 경우 적절히 대응하기가 어렵다.
점층적 생성자 패턴(telescoping constructor pattern)
자바빈즈 패턴(JavaBeans Pattern)
빌더 패턴(Builder Pattenr)
점층적 생성자 패턴의 안정성과 자바빈즈 패턴의 가독성을 겸비함.
계층적으로 설계된 클래스와 함께 쓰기에 좋음.
코드2.3 빌더 패턴 - 점층적 생성자 패턴과 자바빈즈 패펀의 장정만 취했다.
public class NutritionFactsWithCollections {
private final int servingSize ;
private final int servings ;
private final int calories ;
private final int fat ;
private final int sodium ;
private final int carbohydrate ;
private final Set <String > sauces ;
public static NutritionFactsWithCollections .Builder builder (int servingSize , int servings ) {
return new NutritionFactsWithCollections .Builder (servings , servings );
}
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 carbohydrate = 0 ;
private final Set <String > sauces = new HashSet <>();
public Builder (int servingSize , int servings ) {
this .servingSize = servingSize ;
this .servings = servings ;
}
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 Builder addSauces (String sauce ) {
this .sauces .add (sauce );
return this ;
}
public NutritionFactsWithCollections build () {
return new NutritionFactsWithCollections (this );
}
}
private NutritionFactsWithCollections (Builder builder ) {
servingSize = builder .servingSize ;
servings = builder .servings ;
calories = builder .calories ;
fat = builder .fat ;
sodium = builder .sodium ;
carbohydrate = builder .carbohydrate ;
sauces = builder .sauces ;
}
@ Override
public String toString () {
return "NutritionFactsWithCollections{" +
"servingSize=" + servingSize +
", servings=" + servings +
", calories=" + calories +
", fat=" + fat +
", sodium=" + sodium +
", carbohydrate=" + carbohydrate +
", sauces=" + sauces +
'}' ;
}
}
@ Builder
@ AllArgsConstructor (access = AccessLevel .PRIVATE ) //@Builder를 사용하면 전체 인자를 갖는 생성자를 자동으로 만들어준다. 이때 이렇게 private로 선언하도록 만들어주면 생성자로는 인스턴스를 생성하지 못하도록 막을 수 있다.
@ ToString
public class NutritionFactsLombok {
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 class Item02Main {
public static void main (String [] args ) {
//직접 Builder 구현 - Collection이 포함되어 있는 경우
NutritionFactsWithCollections nutritionFactsWithCollections = NutritionFactsWithCollections .builder (10 , 20 )
.calories (100 )
.fat (200 )
.sodium (300 )
.carbohydrate (400 )
.addSauces ("ham" )
.addSauces ("red pepper" )
.build ();
System .out .println ("nutritionFactsWithCollections = " + nutritionFactsWithCollections );
//Lombok 사용
NutritionFactsLombok nutritionFactsLombok = NutritionFactsLombok .builder ()
.servingSize (10 )
.servings (5 )
.fat (100 )
.sodium (200 )
.carbohydrate (300 )
.build ();
System .out .println ("nutritionFactsLombok = " + nutritionFactsLombok );
}
}
빌더 패턴은 계층적으로 설계된 클래스와 함께 쓰기에 좋다.
public class Pizza {
public enum Topping { HAM , MUSHROOM , ONION , PEPPER , SAUSAGE }
final Set <Topping > toppings ;
abstract static class Builder <T extends Builder <T >> {
EnumSet <Topping > toppings = EnumSet .noneOf (Topping .class );
public T addToping (Topping topping ) {
toppings .add (Objects .requireNonNull (topping ));
return self ();
}
abstract Pizza build ();
//하위 클래스는 이 메서드를 재정의(overriding)하여
//"this"를 반환하도록 해야 한다.
protected abstract T self ();
}
Pizza (Builder <?> builder ) {
toppings = builder .toppings .clone ();
}
}
public class MyPizza extends Pizza {
public enum Size { SMALL , MEDIUM , LARGE };
private final Size size ;
public static class Builder extends Pizza .Builder <Builder > {
private final Size size ;
public Builder (Size size ) {
this .size = size ;
}
@ Override
MyPizza build () {
return new MyPizza (this );
}
@ Override
protected Builder self () {
return this ;
}
}
private MyPizza (Builder builder ) {
super (builder );
size = builder .size ;
}
@ Override
public String toString () {
return "MyPizza{" +
"size=" + size +
", toppings=" + toppings +
'}' ;
}
}
// '계츧적 빌더'를 사용하는 클라이언트 코드
MyPizza myPizza = new MyPizza .Builder (MyPizza .Size .SMALL )
.addToping (Pizza .Topping .SAUSAGE )
.addToping (Pizza .Topping .ONION )
.build ();
System .out .println ("myPizza = " + myPizza );