빌더(builder) 패턴
디자인 패턴 ·객체 생성 시 필요한 다양한 매개변수를 설정할 수 있는 방법을 제공하는 디자인 패턴이다
복잡한 객체 생성과정을 숨길 수 있고, 여러 매개변수를 한번에 설정할 수 있다
예시
여행 계획을 생성하는 객체를 만드려고한다. 여행계획에는 여행이름, 시작일, 몇박, 몇일, 숙박업소, 몇일에 무엇을 할건지에 대한 목록을 정할 수 있다.
빌더 패턴 구현 예제
객체를 생성하는 Builder 인터페이스
public interface TourPlanBuilder {
TourPlanBuilder title(String title);
TourPlanBuilder startDate(LocalDate startDate);
TourPlan build();
TourPlanBuilder nightsAndDays(int nights, int days);
TourPlanBuilder whereToStay(String stay);
TourPlanBuilder addPlan(int day, String plan);
}
Concreate Builder
public class DefaultTourPlanBuilder implements TourPlanBuilder {
private final List<DetailPlan> plans;
private String title;
private LocalDate startDate;
private int nights;
private int days;
private String whereToStay;
public DefaultTourPlanBuilder() {
this.plans = new ArrayList<>();
this.title = "";
this.startDate = LocalDate.now();
this.nights = 0;
this.days = 0;
whereToStay = "";
}
@Override
public TourPlanBuilder title(String title) {
this.title = title;
return this;
}
@Override
public TourPlanBuilder startDate(LocalDate startDate) {
this.startDate = startDate;
return this;
}
@Override
public TourPlan build() {
return new TourPlan(this.title, this.nights, this.days, this.startDate, this.whereToStay, this.plans);
}
@Override
public TourPlanBuilder nightsAndDays(int nights, int days) {
this.nights = nights;
this.days = days;
return this;
}
@Override
public TourPlanBuilder whereToStay(String whereToStay) {
this.whereToStay = whereToStay;
return this;
}
@Override
public TourPlanBuilder addPlan(int day, String plan) {
this.plans.add(new DetailPlan(day, plan));
return this;
}
}
Builder에서 생성할 Product
ppublic class TourPlan {
private String title;
private int nights;
private int days;
private LocalDate startDate;
private String whereToStay;
private List<DetailPlan> plans;
public TourPlan() {
}
public TourPlan(String title, int nights, int days, LocalDate startDate, String whereToStay, List<DetailPlan> plans) {
this.title = title;
this.nights = nights;
this.days = days;
this.startDate = startDate;
this.whereToStay = whereToStay;
this.plans = plans;
}
@Override
public String toString() {
return "TourPlan{" +
"title='" + title + '\'' +
", nights=" + nights +
", days=" + days +
", startDate=" + startDate +
", whereToStay='" + whereToStay + '\'' +
", plans=" + plans +
'}';
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getNights() {
return nights;
}
public void setNights(int nights) {
this.nights = nights;
}
public int getDays() {
return days;
}
public void setDays(int days) {
this.days = days;
}
public LocalDate getStartDate() {
return startDate;
}
public void setStartDate(LocalDate startDate) {
this.startDate = startDate;
}
public String getWhereToStay() {
return whereToStay;
}
public void setWhereToStay(String whereToStay) {
this.whereToStay = whereToStay;
}
public List<DetailPlan> getPlans() {
return plans;
}
public void setPlans(List<DetailPlan> plans) {
this.plans = plans;
}
public void addPlan(int day, String plan) {
this.plans.add(new DetailPlan(day, plan));
}
}
public class DetailPlan {
private int day;
private String plan;
public DetailPlan(int day, String plan) {
this.day = day;
this.plan = plan;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public String getPlan() {
return plan;
}
public void setPlan(String plan) {
this.plan = plan;
}
@Override
public String toString() {
return "DetailPlan{" +
"day=" + day +
", plan='" + plan + '\'' +
'}';
}
}
Builder 생성 과정 프리셋같은 Director
public class TourPlanDirector {
private final TourPlanBuilder tourPlanBuilder;
public TourPlanDirector(TourPlanBuilder tourPlanBuilder) {
this.tourPlanBuilder = tourPlanBuilder;
}
public TourPlan longBeachPlan() {
return this.tourPlanBuilder.title("오레곤 롱비치 여행")
.startDate(LocalDate.of(2021, 7, 15))
.build();
}
public TourPlan cancunTrip() {
return this.tourPlanBuilder.title("칸쿤 여행")
.nightsAndDays(2, 3)
.startDate(LocalDate.of(2020, 12, 9))
.whereToStay("리조트")
.addPlan(0, "체크인 이후 짐풀기")
.addPlan(0, "저녁 식사")
.addPlan(1, "조식 부페에서 식사")
.addPlan(1, "해변가 산책")
.addPlan(1, "점심은 수영장 근처 음식점에서 먹기")
.addPlan(1, "저녁은 BBQ 식당에서 스테이크")
.addPlan(2, "조식 부페에서 식사")
.addPlan(2, "체크아웃")
.addPlan(1, "리조트 수영장에서 놀기")
.build();
}
}
Builder 클라이언트 코드
class TourPlanBuilderTest {
@Test
public void build_test() {
TourPlanBuilder builder = new DefaultTourPlanBuilder();
TourPlan plan = builder.title("오레곤 롱비치 여행")
.startDate(LocalDate.of(2021, 7, 15))
.build();
assertEquals("오레곤 롱비치 여행", plan.getTitle());
builder = new DefaultTourPlanBuilder();
TourPlan plan2 = builder.title("칸쿤 여행")
.nightsAndDays(2, 3)
.startDate(LocalDate.of(2020, 12, 9))
.whereToStay("리조트")
.addPlan(0, "체크인 이후 짐풀기")
.addPlan(0, "저녁 식사")
.addPlan(1, "조식 부페에서 식사")
.addPlan(1, "해변가 산책")
.addPlan(1, "점심은 수영장 근처 음식점에서 먹기")
.addPlan(1, "저녁은 BBQ 식당에서 스테이크")
.addPlan(2, "조식 부페에서 식사")
.addPlan(2, "체크아웃")
.addPlan(1, "리조트 수영장에서 놀기")
.build();
assertEquals("칸쿤 여행", plan2.getTitle());
}
}
public class TourPlanDirectorTest {
@Test
public void build_test() {
TourPlanDirector tourPlanDirector = new TourPlanDirector(new DefaultTourPlanBuilder());
TourPlan cancunPlan = tourPlanDirector.cancunTrip();
assertEquals("칸쿤 여행", cancunPlan.getTitle());
TourPlan longBeachPlan = tourPlanDirector.longBeachPlan();
assertEquals("오레곤 롱비치 여행", longBeachPlan.getTitle());
}
}
빌더 패턴은 여러 매개변수를 설정할 때 생기는 코드 중복을 방지할 수 있고
객체 생성을 순서대로 처리하게 강제해 객체를 안전하게 생성할 수 있다.
단, 단순한 객체를 생성할때는 불필요하게 복잡해져, 불필요한 코드양과, 불필요한 메소드 호출이 일어날 수 있다.
예제는 이곳 에서 확인하실수 있습니다.