ITEM 2: Builder Pattern
์์ฑ์์ ์ ์ ํฉํ ๋ฆฌ๋ ์ ํ์ ๋งค๊ฐ๋ณ์๊ฐ ๋ง์ ๋ ์ ์ ํ ๋์ํ๊ธฐ ์ด๋ ต๋ค. ์ ํ์ ๋งค๊ฐ ๋ณ์๊ฐ ๋ง์ ๊ฒฝ์ฐ์ ์ฌ์ฉํ๋ ์์ฑ์ ํจํด์ ๋ํด์ ์ดํด ๋ณผ ๊ฒ์ด๋ค.
์์ฑ์ ํจํด 1. ์ ์ธต์ ์์ฑ์ ํจํด
์ ์ธต์ ์์ฑ์ ํจํด(telescoping constructor pattern)์ ๋ค์๊ณผ ๊ฐ์ด ํ์ ์ธ์๋ฅผ ๋ฐ๋ ์์ฑ์๋ฅผ ์ ์ํ ํ, ์ ํ์ ์ธ์๋ฅผ ํ๋์ฉ ์ถ๊ฐํด๊ฐ๋ฉฐ ์ ์ํ๋ ๊ฒ์ด๋ค.
public class Item {
private final String itemCd; // ํ์
private final String itemNm; // ํ์
private final String ctgId; // ํ์
private final BigDecimal price; // ์ ํ
private final String sellTypeCd; // ์ ํ
public Item(String itemCd, String itemNm, String ctgId){
this(itemCd, itemNm, ctgId, 0);
}
public Item(String itemCd, String itemNm, String ctgId, BigDecimal price){
this(itemCd, itemNm, ctgId, price, "10");
}
public Item(String itemCd, String itemNm, String ctgId, BigDecimal price, String sellTypeCd){
this.itemCd = itemCd;
this.itemNm = itemNm;
this.ctgId = ctgId;
this.price = price;
this.sellTypeCd = sellTypeCd;
}
}
์์์์๋ ์ธ์๊ฐ 5๊ฐ๋ผ ๊ฐ๋จํด ๋ณด์ผ ์ ์์ง๋ง, ๋งค๊ฐ๋ณ์๊ฐ ๋ ๋์ด๋ ์๋ก ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ ์ด๋ ค์์ง๊ณ , ๊ฐ๋ ์ฑ์ด ๋จ์ด์ง๊ฒ ๋๋ค.
์์ฑ์ ํจํด 2. JavaBeans Pattern
์๋ฐ๋น์ฆ ํจํด์ ๋งค๊ฐ๋ณ์๊ฐ ์๋ ์์ฑ์๋ก ๊ฐ์ฒด๋ฅผ ๋ง๋ ํ setter ๋ฉ์๋๋ฅผ ํธ์ถํด ์ํ๋ ๋งค๊ฐ๋ณ์์ ๊ฐ์ ์ค์ ํ๋ ๋ฐฉ์์ด๋ค.
public class Item {
private String itemCd; // ํ์
private String itemNm; // ํ์
private String ctgId; // ํ์
private BigDecimal price; // ์ ํ
private String sellTypeCd; // ์ ํ
public Item() {}
public void setItemCd(String itemCd){ this.itemCd = itemCd; }
public void setItemNm(String itemNm){ this.itemNm = itemNm; }
public void setCtgId(String ctgId){ this.ctgId = ctgId; }
public void setPrice(BigDecimal price){ this.price = price; }
public void setSellTypeCd(String sellTypeCd){ this.sellTypeCd = sellTypeCd; }
}
Item item = new Item();
item.setItemCd("12345678");
item.setItemNm("Effective Java 3/E");
item.setCtgId("9999");
item.setPrice("36000");
item.setSellTypeCd("20");
์๋ฐ๋น์ฆ ํจํด์ ์ ์ธต์ ์์ฑ์ ํจํด์ ๋จ์ ์ ๋ณด์ํด ์ธ์คํด์ค ์์ฑ์ด ์ฝ๊ณ , ๋ ๊ฐ๋ ์ฑ์ด ์ข์์ก๋ค.
ํ์ง๋ง, ์๋ฐ๋น์ฆ ํจํด์์๋ ๊ฐ์ฒด ํ๋๋ฅผ ๋ง๋๋ ค๋ฉด ๋ฉ์๋๋ฅผ ์ฌ๋ฌ ๊ฐ ํธ์ถํด์ผํ๊ณ , ๊ฐ์ฒด๊ฐ ์์ ํ ์์ฑ๋๊ธฐ ์ ๊น์ง๋ ์ผ๊ด์ฑ(consistency)์ด ๋ฌด๋์ง ์ํ์ ์๊ฒ ๋๋ค. ์ผ๊ด์ฑ์ด ๊นจ์ง๋ฏ๋ก ์๋ฐ๋น์ฆ ํจํด์์๋ ํด๋์ค๋ฅผ ๋ถ๋ณ์ผ๋ก ๋ง๋ค ์ ์์ผ๋ฉฐ, ์ค๋ ๋ ์์ ์ฑ์ ์ป์ผ๋ ค๋ฉด ๊ฐ๋ฐ์๊ฐ ์ถ๊ฐ ์์
์ ํด์ค์ผํ๋ค. ์ด๋ฌํ ๋จ์ ์ ๋ณด์ํ๊ธฐ ์ํด freeze
๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์์ผ๋, freeze
๋ฉ์๋๋ฅผ ํ์คํ ํธ์ถํด์คฌ๋์ง ์ปดํ์ผ๋ฌ๊ฐ ๋ณด์ฆํ ๋ฐฉ๋ฒ์ด ์์ด ๋ฐํ์ ์ค๋ฅ์ ์ทจ์ฝํ๋ค.
์์ฑ์ ํจํด 3. Builder Pattern
๋น๋ ํจํด์ ์ ์ธต์ ์์ฑ์ ํจํด์ ์์ ์ฑ๊ณผ ์๋ฐ๋น์ฆ ํจํด์ ๊ฐ๋ ์ฑ์ ๊ฒธ๋นํ ์์ฑ์ ํจํด์ด๋ค.
public class Item {
private final String itemCd; // ํ์
private final String itemNm; // ํ์
private final String ctgId; // ํ์
private final BigDecimal price; // ์ ํ
private final String sellTypeCd; // ์ ํ
public static class Builder {
private final String itemCd; // ํ์
private final String itemNm; // ํ์
private final String ctgId; // ํ์
// ์ ํ์ ๋งค๊ฐ๋ณ์๋ default ๊ฐ์ผ๋ก ์ด๊ธฐํ
private BigDecimal price = BigDecimal.ZERO;
private String sellTypeCd = "00";
public Builder(String itemCd, String itemNm, String ctgId) {
this.itemCd = itemCd;
this.itemNm = itemNm;
this.ctgId = ctgId;
}
public Builder price(BigDecimal price) {
this.price = price;
return this;
}
public Builder sellTypeCd(String sellTypeCd) {
this.sellTypeCd = sellTypeCd;
return this;
}
public Item build() {
return new Item(this);
}
}
private Item(Builder builder) {
itemCd = builder.itemCd;
itemNm = builder.itemNm;
ctgId = builder.ctgId;
price = builder.price;
sellTypeCd = builder.sellTypeCd;
}
}
Item item = new Item.Builder("12345678", "Effective Java 3/E", "9999").price(36000).sellTypeCd("90").build();
ํด๋ผ์ด์ธํธ๋ ํ์ ๋งค๊ฐ๋ณ์๋ง์ผ๋ก ์์ฑ์๋ฅผ ํธ์ถํด ๋น๋ ๊ฐ์ฒด๋ฅผ ์ป๊ณ , ๋น๋ ๊ฐ์ฒด๊ฐ ์ ๊ณตํ๋ setter ๋ฉ์๋๋ค๋ก ์ํ๋ ์ ํ ๋งค๊ฐ๋ณ์๋ค์ ์ค์ ํ ์ ์๋ค. ๋ง์ง๋ง์ผ๋ก ๋งค๊ฐ๋ณ์๊ฐ ์๋ build()
๋ฉ์๋๋ฅผ ํธ์ถํด ํ์ํ ๊ฐ์ฒด๋ฅผ ์ป์ ์ ์๋ค. ์ด๋ ๊ฒ ์ฐ์์ ์ผ๋ก ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๋ฐฉ๋ฒ์ fluent API or method chaining์ด๋ผ ํ๋ค.
๋ถ๋ณ : ์ด๋ ํ ๋ณ๊ฒฝ๋ ํ์ฉํ์ง ์๋๋ค. ๋ํ์ ์ผ๋ก String ๊ฐ์ฒด๋ ํ๋ฒ ๋ง๋ค์ด์ง๋ฉด ์ ๋ ๊ฐ์ ๋ฐ๊ฟ ์ ์๋ ๋ถ๋ณ ๊ฐ์ฒด
๋ถ๋ณ์ : ํ๋ก๊ทธ๋จ์ด ์คํ๋๋ ๋์(ํน์ ์ ํด์ง ๊ธฐ๊ฐ) ๋ฐ๋์ ๋ง์กฑํด์ผํ๋ ์กฐ๊ฑด์ ๋งํ๋ค. ๋ณ๊ฒฝ์ ํ์ฉํ ์ ๋ ์์ผ๋, ์ฃผ์ด์ง ์กฐ๊ฑด ๋ด์์๋ง ํ์ฉํ๋ค๋ ๋ป์ด๋ค.
๋ถ๋ณ์์ ๋ณด์ฅํ๊ธฐ ์ํด์๋ ๋น๋๋ก ๋ถํฐ ๋งค๊ฐ๋ณ์๋ฅผ ๋ณต์ฌํ ํ ํด๋น ๊ฐ์ฒด ํ๋๋ ๊ฒ์ฌํด์ผํ๋ค.(item50) ๊ฒ์ฌ์ ์๋ชป๋ ์ ์ ๋ฐ๊ฒฌํ๋ฉด ์ด๋ค ๋งค๊ฐ๋ณ์๊ฐ ์๋ชป๋์๋์ง์ ๋ํ ๋ฉ์ธ์ง๋ฅผ ๋ด์ IllegalArgumentException
(item75) ์ค๋ฅ ๋ฐ์์ ํด์ฃผ๋ฉด๋๋ค.
๊ณ์ธต์ ์ผ๋ก ์ค๊ณ๋ ํด๋์ค
๋น๋ ํจํด์ ๊ณ์ธต์ ์ผ๋ก ์ค๊ณ๋ ํด๋์ค์ ์ฌ์ฉํ๊ธฐ์ ์ข๋ค.
public abstract class Allnco {
public enum ApiType { ADD_ITEM, UPDATE_ITEM, UPDATE_IMAGE, UPDATE_PRC }
final Set<ApiType> apiTypes;
abstract static class Builder<T extends Builder<T>> {
EnumSet<ApiType> apiTypes = EnumSet.noneOf(ApiType.class);
public T addApiType(ApiType apiType){
apiTypes.add(Objects.requireNonNull(apiType));
return self();
}
abstract Allnco build();
// ํ์ ํด๋์ค๋ ์ด ๋ฉ์๋๋ฅผ overridingํด "this"๋ฅผ ๋ฐํํ๋๋ก ๊ตฌํํด์ผํจ.
protected abstract T self();
}
Allnco(Builder<?> builder){
apiTypes = builder.apiTypes.clone();
}
}
์ฌ๊ธฐ์ Allnco.Builder ํด๋์ค๋ ์ฌ๊ท์ ํ์
ํ์ ์ ์ด์ฉํ๋ ์ ๋ค๋ฆญ ํ์
์ด๋ค. ์ฌ๊ธฐ์ ์ถ๊ฐ์ ์ผ๋ก ์ถ์ ๋ฉ์๋์ธ self()
๋ฅผ ์ถ๊ฐํด ํ์ ํด๋์ค์์ ํ ๋ณํ ํ์ง ์๊ณ ๋ method chaining์ ํ ์ ์๋ค.
public class Gmarket extends Allnco{
public enum Chnl { ONLINE, OUTLET, MART, DEPARTMENT, BUYING }
private final Chnl chnl; // final -> immutable
public static class Builder extends Allnco.Builder<Builder> {
private final Chnl chnl;
public Builder(Chnl chnl){
this.chnl = Objects.requireNonNull(chnl);
}
@Override
public Gmarket build(){
return new Gmarket(this);
}
@Override
protected Builder self(){
return this;
}
}
private Gmarket(Builder builder){
super(builder);
chnl = builder.chnl;
}
}
public class Naver extends Allnco{
private final boolean isHapi;
public static class Builder extends Allnco.Builder<Builder> {
public boolean isHapi = false;
public Builder connectToHapi(){
isHapi = true;
return this;
}
@Override
public Naver build(){
return new Naver(this);
}
@Override
protected Builder self(){
return this;
}
}
private Naver(Builder builder){
super(builder);
isHapi = builder.isHapi;
}
}
Gmarket gmarket = new Gmarket.Builder(Gmarket.Chnl.MART).addApiType(Gmarket.ApiType.UPDATE_ITEM).addApiType(Gmarket.ApiType.UPDATE_PRC).build();
Naver naver = new Naver.Builder().addApiType(Naver.ApiType.ADD_ITEM).connectToHapi().build();
๊ฐ๊ฐ์ ํ์ ํด๋์ค์ ๋น๋๊ฐ ์ ์ํ build
๋ฉ์๋๋ ํด๋น ํ์ ํด๋์ค(Naver
, Gmarket
)์ ๋ฐํํ๋๋ก ๋์ด์๋ค. ์ด๋ ๊ฒ ํ์ ํด๋์ค์ ๋ฉ์๋๊ฐ ์์ ํด๋์ค๊ฐ ์ ์ํ ๋ฆฌํด ํ์
์ด ์๋, ๊ทธ ํ์ ํ์
์ ๋ฆฌํดํ๋ ๊ฒ์ Convariant return typing(๊ณต๋ณ ๋ฐํ ํ์ดํ)์ด๋ผ ํ๋ค. ์ด ๊ธฐ๋ฅ์ผ๋ก ํด๋ผ์ด์ธํธ๊ฐ ํ๋ณํ์ ์ ๊ฒฝ ์ฐ์ง ์๊ณ ๋น๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
๊ฒฐ๋ก
๋น๋ ํจํด์ ๋น๋ ํ๋๋ก ์ฌ๋ฌ ๊ฐ์ฒด๋ฅผ ๋ง๋ค ์ ์๊ณ , ๋น๋์ ๋๊ธฐ๋ ๋งค๊ฐ๋ณ์์ ๋ฐ๋ผ ๋ค๋ฅธ ๊ฐ์ฒด๋ฅผ ๋ง๋ค ์ ์์ผ๋ฏ๋ก ๋งค์ฐ ์ ์ฐํ๋ค.
ํ์ง๋ง ๊ฐ์ฒด๋ฅผ ๋ง๋ค๋ ค๋ฉด, ๊ทธ์ ์์ ๋น๋๋ถํฐ ๋ง๋ค์ด์ผํ๋ค. ๋ํ, ์ฑ๋ฅ์ ๋ฏผ๊ฐํ ์ํฉ์์๋ ๋น๋ ์์ฑ ๋น์ฉ์ด ๋ฌธ์ ๊ฐ ๋ ์ ์๋ค. ๋ํ ๋งค๊ฐ๋ณ์๊ฐ 4๊ฐ ์ด์์ด ๋์ด์ผ ๊ฐ์ด์น๋ฅผ ํ๋ค.
์ฆ, ์ธ์๊ฐ ๋ง์ ์์ฑ์๋ ์ ์ ํฉํฐ๋ฆฌ๊ฐ ํ์ํ ํด๋์ค๋ฅผ ์ค๊ณํ ๋, ๋๋ถ๋ถ์ ์ธ์๊ฐ ์ ํ์ ์ธ์์ธ ์ํฉ์ ์ ์ฉํ๋ค. ๋น๋๋ ์ ์ธต์ ์์ฑ์๋ณด๋ค ๊ฐ๊ฒฐํ๊ณ , ์๋ฐ๋น์ฆ๋ณด๋ค ํจ์ฌ ์์ ํ๋ค.
Last updated
Was this helpful?