Effective Java - Item 2
μ μ ν©ν 리μ μμ±μλ μ νμ 맀κ°λ³μκ° λ§μ λμλ λμνκΈ° μ΄λ ΅λ€λ λμΌν μ μ½μ΄ μ‘΄μ¬νλ€. μ΄λ° λ¬Έμ μ ν΄κ²°μ± μΌλ‘ μΈ κ°μ§ ν¨ν΄μ λ€ μ μλλ°, λ°λ‘ μ μΈ΅μ μμ±μ ν¨ν΄, μλ°λΉμ¦ ν¨ν΄ κ·Έλ¦¬κ³ λΉλ ν¨ν΄μ΄λ€.
β μ μΈ΅μ μμ±μ ν¨ν΄ (Telescoping Constructor Pattern)μ΄λ?
νμ 맀κ°λ³μλ§ λ°λ μμ±μ, νμ 맀κ°λ³μμ μ ν 맀κ°λ³μ 1κ°λ₯Ό λ°λ μμ±μ, μ ν 맀κ°λ³μλ₯Ό 2κ° λ°λ μμ±μ ννλ‘ μ ν 맀κ°λ³μλ₯Ό μ λΆ λ€ λ°λ μμ±μκΉμ§ λλ €κ°λ λ°©μμ΄λ€.
μ½λ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
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, 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; } }
β ν΄λΉ ν΄λμ€μμ νμ 맀κ°λ³μλ 2κ°λΏμ΄μ§λ§, μ΄μΈμ μ ν 맀κ°λ³μλ€λ μ΄μ© μ μμ΄ κ°μ μ§μ ν΄μ€μΌ νκΈ° λλ¬Έμ ν¨μ¨μ΄ λ¨μ΄μ§λ€.
νμ§λ§ 맀κ°λ³μ κ°μκ° λ§μμ§λ©΄ λ§μμ§μλ‘ λ§μ μ‘°ν©μ΄ λ§λ€μ΄μ§κ³ , μμ±μμ μκ° λ§μμ Έ ν΄λΌμ΄μΈνΈ μ½λ μμ± ν¨μ¨κ³Ό κ°λ μ±μ΄ λ¨μ΄μ§λ€.
λν μΈμλ‘ λ°λ 맀κ°λ³μ νμ μ΄ κ°μ κ²½μ°μλ μμ±μλ₯Ό λ§λ€ μ μμ λΏλλ¬ μμ±μλ₯Ό νΈμΆνλ μ μ₯μμλ ν΄λΉ 맀κ°λ³μμ κ°μκ° λ§λμ§ νμ νμΈ ν΄μΌνλ€λ λΆνΈν¨μ΄ λ°λ₯Έλ€.
β μλ°λΉμ¦ ν¨ν΄ (JavaBeans Pattern)μ΄λ?
맀κ°λ³μκ° μλ μμ±μλ‘ κ°μ²΄λ₯Ό λ§λ ν, μΈν°(setter) λ©μλλ€μ νΈμΆν΄ μνλ 맀κ°λ³μμ κ°μ μ€μ νλ λ°©μμ΄λ€. μ½λλ κΈΈμ΄μ§μ§λ§ μ μΈ΅μ μμ±μ ν¨ν΄μ λΉν΄ μΈμ€ν΄μ€λ₯Ό λ§λ€κΈ° μ½κ³ κ°λ μ± λν μ’λ€.
μ½λ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
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 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; } }
β ν΄λΉ ν΄λμ€μμ νμ 맀κ°λ³μλ 2κ°λΏμ΄μ§λ§, μ΄μΈμ μ ν 맀κ°λ³μλ€λ μ΄μ© μ μμ΄ κ°μ μ§μ ν΄μ€μΌ νκΈ° λλ¬Έμ ν¨μ¨μ΄ λ¨μ΄μ§λ€.
κ·Έλ¬λ κ°μ²΄ νλλ₯Ό μμ±νλ €λ©΄ λ©μλλ₯Ό μ¬λ¬ κ° νΈμΆν΄μΌ νλ€λ λ¨μ λ μ‘΄μ¬νλ€. λν ν λ²μ μμ±μ νΈμΆλ§μΌλ‘ μμ±μ΄ μλ£λμ§ μκΈ° λλ¬Έμ μΌκ΄μ±(consistency)μ΄ λ¬΄λμ§κ³ λ³νμ§ μλ λΆλ³(immutable) κ°μ²΄λ₯Ό λ§λ€ μ μλ€.
λ§μ§λ§μΌλ‘, μ μΈ΅μ μμ±μ ν¨ν΄μ μμ μ±κ³Ό μλ°λΉμ¦ ν¨ν΄μ κ°λ μ±μ κ²°ν©ν λμμΈ ν¨ν΄μΈ λΉλ ν¨ν΄μ΄ μ‘΄μ¬νλ€.
β λΉλ ν¨ν΄ (Builder Pattern)μ΄λ?
νμ 맀κ°λ³μλ§μΌλ‘ μ μ ν©ν 리 λ©μλλ₯Ό μ΄μ©ν΄ λΉλ κ°μ²΄λ₯Ό μ»μ λ€, λΉλ κ°μ²΄κ° μ 곡νλ μΈν° λ©μλλ€λ‘ νμν μ ν 맀κ°λ³μλ₯Ό μ λ ₯ν μ μλ€.
κ°κ°μ μΈν° λ©μλλ κ°μ μ€μ ν λ€ μκΈ° μμ (Builder)μ λ°ννκΈ° λλ¬Έμ μ°μν΄μ λ©μλ νΈμΆμ΄ κ°λ₯νκ³ , λ§μ§λ§μΌλ‘ build()
λ©μλλ₯Ό μ¬μ©ν΄ νμν κ°μ²΄λ₯Ό μμ±νμ¬ λ°νν μ μλ€.
μ½λ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
package effectiveJava.item02; public class NutritionFacts3 { 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 = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 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 sodium(int val) { sodium = val; return this; } public Builder carbohydrate(int val) { carbohydrate = val; return this; } public NutritionFacts3 build() { return new NutritionFacts3(this); } } private NutritionFacts3(Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbohydrate = builder.carbohydrate; } }
β μ μΈ΅μ μμ±μ ν¨ν΄μ λΉν΄ νμν μΈμ€ν΄μ€λ€λ§ μμ±νκΈ° μ½λ€.
π κ³μΈ΅μ μΌλ‘ μ€κ³λ ν΄λμ€μ λΉλ ν¨ν΄ μ¬μ© μμ
μ½λ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
package effectiveJava.item02; import java.util.Objects; import java.util.EnumSet; 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(); // νμ ν΄λμ€λ μ΄ λ©μλλ₯Ό μ€λ²λΌμ΄λ©νμ¬ "this"λ₯Ό λ°νν΄μΌνλ€. protected abstract T self(); } Pizza(Builder<?> builder) { toppings = builder.toppings.clone(); } }
β μΆμ λ©μλ self() λ νμ ν΄λμ€μμ νλ³νμ νμ§ μκ³ λ μκΈ° μμ μ 리ν΄νμ¬ λ©μλλ₯Ό μ°μμ μΌλ‘ νΈμΆν μ μλλ‘ νλ€.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
package effectiveJava.item02; 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 = size; } @Override NyPizza build() { return new NyPizza(this); } @Override protected Builder self() { return this; } } private NyPizza(Builder builder) { super(builder); size = builder.size; } }
β Pizza μ νμ ν΄λμ€, ν¬κΈ°(size)λ₯Ό νμ 맀κ°λ³μλ‘ λ°λλ€.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
package effectiveJava.item02; public class Calzone extends Pizza { private final boolean sauceInside; public static class Builder extends Pizza.Builder<Builder> { private boolean sauceInside = false; public Builder sauceInside() { this.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 μ νμ ν΄λμ€, μμ€ μ ν(sauceInside)λ₯Ό νμ 맀κ°λ³μλ‘ λ°λλ€.
1 2 3 4
NyPizza pizza = new NyPizza.Builder(SMALL) .addTopping(ONION).build(); Calzone calzone = new Calzone.Builder().sauceInside() .addTopping(HAM).build();
β NyPizzaμ Calzone κ°μ²΄ μμ±
μμ μ½λμμ
NyPizza.Builder
λNyPizza
λ₯Ό λ°ννκ³Calzone.Builder
λCalzone
λ₯Ό λ°ννλ€. μ΄μ²λΌ νμ ν΄λμ€μ λ©μλκ° μμ ν΄λμ€μ λ©μλμμ μ μν λ°ν νμ μ΄ μλ, κ·Έ νμ νμ μ λ°ννλ κΈ°λ₯μ κ³΅λ³ λ°ν νμ΄ν(convariant return typing)μ΄λΌκ³ νλ€.<JDK 1.5 μ΄μ> ν΄λΉ κΈ°λ₯ λλΆμ ν΄λΌμ΄μΈνΈλ νλ³νμ μ κ²½ μ°μ§ μκ³ λΉλλ₯Ό μ¬μ©ν μ μλ€.
λ¬Όλ‘ λΉλ ν¨ν΄λ λ¨μ μ΄ μλ κ²μ μλλ€. κ°μ²΄λ₯Ό μμ±νκΈ° μν΄μλ λ¨Όμ λΉλ ν΄λμ€λΆν° μ μν΄μΌ νλ€. λΉλμ μμ± λΉμ©μ΄ ν° κ²μ μλμ§λ§, μ±λ₯μ λ―Όκ°ν μν©μμλ λ¬Έμ κ° λ μ μλ€.
λν 맀κ°λ³μκ° λͺ κ° μκ³ , λ³κ²½λ κ°λ₯μ±μ΄ μμ μλ€λ©΄ μ½λκ° λ€μ μ₯ν©ν λΉλ ν¨ν΄λ³΄λ€λ μ μΈ΅μ μμ±μ ν¨ν΄μ μ¬μ©νλ κ²μ΄ λ μ 리νλ€.
κ·Έλ¦¬κ³ μΆκ°λ‘ λΉλ ν¨ν΄μ μμ±νλ μ½κ³ νΈλ¦¬ν λ°©λ²μ΄ μ‘΄μ¬νλλ° λ°λ‘ Lombok λΌμ΄λΈλ¬λ¦¬μ @Builder
μ΄λ
Έν
μ΄μ
μ΄λ€. @Builder
μ΄λ
Έν
μ΄μ
μ λͺ¨λ λ©€λ² νλμ λν΄μ 맀κ°λ³μλ₯Ό λ°λ κΈ°λ³Έ μμ±μλ₯Ό λ§λ€μ΄μ£ΌκΈ° λλ¬Έμ λ°λ‘ Builder Classλ₯Ό μμ±ν νμκ° μμ΄ μ½λ μμ±μ λ§€μ° μ©μ΄νλ€.
λ€λ§ μ κ·Ό λ λ²¨μ΄ defaultμ΄κΈ° λλ¬Έμ, λμΌ ν¨ν€μ§ λ΄μμ ν΄λΉ μμ±μλ₯Ό νΈμΆ ν μ μλ λ¬Έμ κ° μκΈ΄λ€. κ·Έλ¬λ―λ‘ κ°μ²΄ μμ± μ λ°μμΌ νλ 맀κ°λ³μλ€λ§ μ‘΄μ¬νλ μμ±μλ₯Ό λ§λ€κ³ κ·Έ μμ±μμ @Builder
λ₯Ό μ§μ νλ κ²μ΄ λ°λμ§νλ€.