๊ท์น2 : ์์ฑ์ ์ธ์๊ฐ ๋ง์ ๋๋ Builder ํจํด ์ ์ฉ์ ๊ณ ๋ คํ๋ผ
๊ท์น2 : ์์ฑ์ ์ธ์๊ฐ ๋ง์ ๋๋ Builder ํจํด ์ ์ฉ์ ๊ณ ๋ คํ๋ผ ๊ด๋ จ
Consider a builder when faced with many constructor parameters
์ ํ์ ์ธ์๊ฐ ๋ง์ ์ํฉ์์ ์ด๋ค ์์ฑ์๋ ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋๊ฐ ์ ํฉํ ๊น?
์ ์ธต์ ์์ฑ์ ํจํด (telescoping constructor pattern)
ํ์ ์ธ์๋ง ๋ฐ๋ ์์ฑ์๋ฅผ ํ๋ ์ ์ํ๊ณ , ์ ํ์ ์ธ์๋ฅผ ํ๋ ๋ฐ๋ ์์ฑ์๋ฅผ ์ถ๊ฐํ๊ณ ๊ฑฐ๊ธฐ์ ๋๊ฐ์ ์ ํ์ ์ธ์๋ฅผ ๋ฐ๋ ์์ฑ์๋ฅผ ์ถ๊ฐํ๋ ์์ผ๋ก ์์ฑ์๋ค์ ์์ ์ฌ๋ฆฌ๋ฏ ์ถ๊ฐํ๋ ๊ฒ์ด๋ค.
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(servingSizem servings, 0);
}
public NutritionFacts(int servingSize, int servings, int calories){
this(servingSizem servings, calories, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat){
this(servingSizem servings, calories, fat, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat,
int sodium){
this(servingSizem 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 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() {}
//์ค์ ์(setter)
public void setServingSize(int val) { servingSize = val; }
public void setServings(int val) { servings = 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; }
}
์ด ํจํด์๋ ์ ์ธต์ ์์ฑ์ ํจํด์ ์๋ ๋ฌธ์ ๋ ์๋ค. ์์ฑํด์ผ ํ๋ ์ฝ๋์ ์์ด ์กฐ๊ธ ๋ง์์ง ์๋ ์์ง๋ง ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ธฐ๋ ์ฌ์ฐ๋ฉฐ, ์ฝ๊ธฐ๋ ์ข๋ค.
NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);
๊ทธ๋ฌ๋ ์๋ฐ๋น ํจํด์๋ ์ฌ๊ฐํ ๋จ์ ์ด ์๋ค. 1ํ์ ํจ์ ํธ์ถ๋ก ๊ฐ์ฒด ์์ฑ์ ๋๋ผ ์ ์์ผ๋ฏ๋ก, ๊ฐ์ฒด ์ผ๊ด์ฑ์ด ์ผ์์ ์ผ๋ก ๊นจ์ง ์ ์๋ค๋ ๊ฒ์ด๋ค. ๋ํ ์๋ฐ๋น ํจํด์ผ๋ก๋ ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅ ํด๋์ค๋ฅผ ๋ง๋ค ์ ์๋ค๋ ๊ฒ์ด๋ค.
๋น๋ (Builder) ํจํด
์ ์ธต์ ์์ฑ์ ํจํด์ ์์ ์ฑ๊ณผ ์๋ฐ๋น ํจํด์ ๊ฐ๋ ์ฑ์ ๊ฒฐํฉํ ํจํด์ด๋ค.
ํ์ํ ๊ฐ์ฒด๋ฅผ ์ง์ ์์ฑํ๋ ๋์ , ํด๋ผ์ด์ธํธ๋ ๋จผ์ ํ์ ์ธ์๋ค์ ์์ฑ์์(๋๋ ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋์) ์ ๋ถ ์ ๋ฌํ์ฌ ๋น๋ ๊ฐ์ฒด(builder object)๋ฅผ ๋ง๋ ๋ค. ๊ทธ๋ฐ ๋ค์ ๋น๋ ๊ฐ์ฒด์ ์ ์๋ ์ค์ ๋ฉ์๋๋ค์ ํธ์ถํ์ฌ ์ ํ์ ์ธ์๋ค์ ์ถ๊ฐํด ๋๊ฐ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง์ผ๋ก ์๋ฌด๋ฐ ์ธ์ ์์ด 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 carbohydrate;
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
public static class Builder {
//ํ์์ธ์
private final int servingSize;
private final int servings;
//์ ํ์ ์ธ์ - ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ด๊ธฐํ
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 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 carbohydrate(int val) {
carbohydrate = val;
return this;
}
public Builder sodium(int val) {
sodium = val;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
}
NutritionFacts
๊ฐ์ฒด๊ฐ ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ๋ค๋ ์ฌ์ค, ๊ทธ๋ฆฌ๊ณ ๋ชจ๋ ์ธ์์ ๊ธฐ๋ณธ๊ฐ์ด ํ๊ณณ์ ๋ชจ์ฌ ์๋ค๋ ๊ฒ์ ์ ์ํด๋ผ. ๋น๋์ ์ ์๋ ์ค์ ๋ฉ์๋๋ ๋น๋ ๊ฐ์ฒด ์์ ์ ๋ฐํํ๋ฏ๋ก, ์ค์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ์ฝ๋๋ ๊ณ์ ์ด์ด์ ์ธ ์ ์๋ค.
NutirtionFacts cocaCola = new NutritionFacts.Builder(240,8)
.calories(100)
.sodium(35)
.carbohydrate(27)
.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 carbohydrate;
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
if (servingSize > 0) {
throw new IllegalArgumentException();
}
...
}
3rd edition ์ถ๊ฐ๋ ๋น๋ํจํด ์์
import java.util.EnumSet;
import java.util.Objects;
import java.util.Set;
public abstract 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 addTopping(Topping topping) {
toppings.add(Objects.requireNonNull(topping));
return self();
}
abstract Pizza build();
// Subclasses must override this method to return "this"
protected abstract T self();
}
Pizza(Builder<?> builder) {
toppings = builder.toppings.clone();
}
}
Pizza
์ถ์ ํด๋์ค์ ๊ทธ์์ Builder
์ถ์ ํด๋์ค๋ฅผ ๋ง๋ค์๋ค.
import java.util.Objects;
public class NyPizza 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 = Objects.requireNonNull(size);
}
@Override
NyPizza build() {
return new NyPizza(this);
}
@Override
protected Builder self() {
return this;
}
}
private NyPizza(Builder builder) {
super(builder);
size = builder.size;
}
}
public class Calzone extends Pizza {
private final boolean sauceInside;
public static class Builder extends Pizza.Builder<Builder> {
private boolean sauceInside = false; // default
public Builder sauceInside() {
sauceInside = true;
return this;
}
@Override
Calzone build() {
return new Calzone(this);
}
@Override
protected Builder self() {
return this;
}
}
private Calzone(Builder builder) {
super(builder);
sauceInside = builder.sauceInside;
}
}
์ฃผ๋ชฉํด์ผ ๋ ๋ถ๋ถ์ Pizza
์ถ์ ํด๋์ค๋ฅผ ์์ํ NyPizza
, Calzone
ํด๋์ค์์ ์ค๋ฒ๋ผ์ด๋ฉํ build
๋ฉ์๋ return type์ด ์๊ธฐ ์์ ์ด๋ค(Pizza
๊ฐ ์๋๋ผ). ์ด๋ ๊ฒ ํจ์ผ๋ก์จ ์ฌ์ฉํ ๋ ํ์
์บ์คํ
์ ๋ฐ๋ก ์ํด์ค๋ ๋๋ค.
public class BuilderMain {
public static void main(String[] args) {
NyPizza pizza = new NyPizza.Builder(NyPizza.Size.SMALL)
.addTopping(Pizza.Topping.SAUSAGE)
.addTopping(Pizza.Topping.ONION)
.build();
Calzone calzone = new Calzone.Builder()
.addTopping(Pizza.Topping.HAM)
.sauceInside()
.build();
}
}
์์ฝ
๋น๋ ํจํด์ ์ธ์๊ฐ ๋ง์ ์์ฑ์(4๊ฐ ์ด์)๋ ์ ์ ํฉํ ๋ฆฌ๊ฐ ํ์ํ ํด๋์ค๋ฅผ ์ค๊ณํ ๋, ํนํ ๋๋ถ๋ถ์ ์ธ์๊ฐ ์ ํ์ ์ธ์์ธ ์ํฉ์ ์ ์ฉํ๋ค.