https://inpa.tistory.com/entry/GOF-💠-빌더Builder-패턴-끝판왕-정리
<aside> 💡 빌더 패턴(Builder Pattern)
복잡한 객체의 생성 과정과 표현 방법을 분리하여 다양한 구성의 인스턴스를 만드는 생성 패턴이다. 생성자에 들어갈 매개 변수를 메서드로 하나하나 받아들이고 마지막에 통합 빌드해서 객체를 생성하는 방식
서브웨이 사례 재료가 주문하는 사람 마음대로 결정되므로 선택적 재료들을 보다 유연하게 받아들여 다양한 타입의 인스턴스를 생성할 수 있어 클래스의 선택적 매개변수가 많은 상황에서 유용하게 사용됨
</aside>
점층적 생성자 패턴(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;
}
}
자바빈즈 패턴은 매개변수가 없는 생성자로 객체를 만든 후 setter 메서드를 호출해 원하는 매개변수의 값을 설정하는 방식
public class Item {
private String itemCd; // 필수
private String itemNm; // 필수
private String ctgId; // 필수
private BigDecimal price; // 선택
private String sellTypeCd; // 선택
private boolean frozen;
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");
freeze
메서드를 사용할 수 있으나, freeze
메서드를 확실히 호출해줬는지 컴파일러가 보증할 방법이 없어 런타임 오류에 취약빌더 패턴은 점층적 생성자 패턴의 안정성과 자바빈즈 패턴의 가독성을 겸비한 생성자 패턴
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 객체는 한번 만들어지면 절대 값을 바꿀 수 없는 불변 객체
불변식 : 프로그램이 실행되는 동안(혹은 정해진 기간) 반드시 만족해야하는 조건을 말한다. 변경을 허용할 수 는 잇으나, 주어진 조건 내에서만 허용한다는 뜻
불변식을 보장하기 위해서는 빌더로 부터 매개변수를 복사한 후 해당 객체 필드도 검사해야함
// 생성자 방식
Student student1 = new Student(2016120091, "홍길동", "freshman", "010-5555-5555");
// 빌더 방식
Student student2 = new StudentBuilder()
.id(2016120091)
.name("임꺽정")
.grade("Senior")
.phoneNumber("010-5555-5555")
.build();