728x90
빌더(Builder)패턴
- 동일한 프로세스를 거쳐 다양한 구성의 인스턴스를 만드는 방법
- (복잡한)객체를 만드는 프로세스를 독립적으로 분리할 수 있다.
정의(Definition)
- GoF 디자인 패턴 중 생성 패턴에 해당한다.
- 빌더 패턴은 복잡한 객체를 생성하는 클래스와 표현하는 클래스를 분리하여, 동일한 절차에서도 서로 다른 표현을 생성하는 방법을 제공
- 생성해야하는 객체가 Optional한 속성을 많이 가질 때 더 좋다.
해결하고자 하는 문제
- 객체를 생성할 때 생성자(Constructor)만 사용할 때 발생할 수 있는 문제를 개선하기 위해 고안
- 팩토리 메소드 패턴이나 추상 팩토리 패턴에서는 생성해야하는 클래스에 대한 속성값이 많을 때 아래와 같은 이슈들이 있다.
1. 클라이언트 프로그램에서 팩토리 클래스를 호출할때 Optional한 인자가 많아지면, 타입과 순서에 대한 관리가 어려워져 에러발생 확률이 높아진다.
2. 경우에 따라 필요없는 파라미터들에 대해서 팩토리 클래스에 일일이 NULL값을 넘겨줘야한다.
3. 생성해야 하는 sub class가 무거워지고 복잡해짐에 따라 팩토리 클래스 또한 복잡해진다.
빌더 패턴 요구사항
- 불필요한 생성자를 만들지 않고 객체를 만든다.
- 데이터의 순서에 상관없이 객체를 만들어 낸다.
- 사용자가 봤을때 명시적이고 이해할 수 있어야한다.
점층적 생성자패턴
- 점층적 생성자 패턴을 적용하면 생성자 오버로딩을 통해 구현
- 아래와 같이 점층적 생성자 패턴으로 구현하면 Optional한 인자에 따라 새로운 생성자를 만들거나,Null값으로 채워야하는 이슈가 발생
/**
* 기본 생성자 (필수)
*/
public TourPlan() {
}
/**
* 일반적인 여행 계획 생성자
*
* @param title 여행 제목
* @param startDate 출발 일
* @param nights n박
* @param days m일
* @param whereToStay 머물 장소
* @param plans n일차 할 일
*/
public TourPlan(String title, LocalDate startDate, int nights, int days,
String whereToStay, List<DetailPlan> plans) {
this.title = title;
this.nights = nights;
this.days = days;
this.startDate = startDate;
this.whereToStay = whereToStay;
this.plans = plans;
}
/**
* 당일치기 여행 계획 생성자
*
* @param title 여행 제목
* @param startDate 출발 일
* @param plans 1일차 할 일
*/
public TourPlan(String title, LocalDate startDate, List<DetailPlan> plans) {
this.title = title;
this.startDate = startDate;
this.plans = plans;
}
// 순서를 파악이 어렵고, 가독성이 떨어진다.
new TourPlan("여행 계획", LocalDate.of(2021,12, 24), 3, 4, "호텔",
Collections.singletonList(new DetailPlan(1, "체크인")));
// 생성자를 만들지 않고 당일치기 객체를 생성하면 불필요한 Null을 채워야한다.
new TourPlan("여행 계획", LocalDate.of(2021,12, 24), null, null, null,
Collections.singletonList(new DetailPlan(1, "놀고 돌아오기")));
자바 빈(Bean)패턴
- setter메소드를 사용한 자바빈(Bean)패턴을 고안
- 가독성이 좋아지고, 순서에도 자유롭기 때문에 에러 발생 가능성도 줄어듬
TourPlan tourPlan = new TourPlan();
tourPlan.setTitle("칸쿤 여행");
tourPlan.setNights(2);
tourPlan.setDays(3);
tourPlan.setStartDate(LocalDate.of(2021, 12, 24));
tourPlan.setWhereToStay("리조트");
tourPlan.addPlan(1, "체크인 이후 짐풀기");
tourPlan.addPlan(1, "저녁 식사");
tourPlan.addPlan(2, "조식 부페에서 식사");
tourPlan.addPlan(2, "해변가 산책");
tourPlan.addPlan(2, "점심은 수영장 근처 음식점에서 먹기");
tourPlan.addPlan(2, "리조트 수영장에서 놀기");
tourPlan.addPlan(2, "저녁은 BBQ 식당에서 스테이크");
tourPlan.addPlan(3, "조식 부페에서 식사");
tourPlan.addPlan(3, "체크아웃");
해결하기 못한 문제점
- 함수 호출이 인자만큼 이루어지고, 객체 호출 한번에 생성할 수 없다.
- immutable 객체를 생성 할 수 없다. (setter로 값 변경 가능)
- 쓰레드간 공유 가능한 객체 일관성(consistency)이 일시적으로 깨질 수 있다.
빌더패턴
- 생성자 패턴과 자바 빈 패턴의 장점을 결합하여 객체 생성과 관련된 문제 해결
- 필요한 객체를 직접 생성하는 대신, 먼저 필수 인자들을 생성자에 전부 전달하여 빌더 객체를 만든다.
- 그리고 선택 인자는 가독성이 좋은 코드로 인자를 넘길 수 있다.
- setter가 없으므로 객체 일관성을 유지하여 불변 객체로 생성할 수 있다.
public interface TourPlanBuilder {
TourPlanBuilder nightsAndDays(int nights, int days);
TourPlanBuilder title(String title);
TourPlanBuilder startDate(LocalDate localDate);
TourPlanBuilder whereToStay(String whereToStay);
TourPlanBuilder addPlan(int day, String plan);
TourPlan getPlan();
}
//ConcreteBuilder
public class DefaultTourBuilder implements TourPlanBuilder {
private String title;
private int nights;
private int days;
private LocalDate startDate;
private String whereToStay;
private List<DetailPlan> plans;
@Override
public TourPlanBuilder nightsAndDays(int nights, int days) {
this.nights = nights;
this.days = days;
return this;
}
@Override
public TourPlanBuilder title(String title) {
this.title = title;
return this;
}
@Override
public TourPlanBuilder startDate(LocalDate startDate) {
this.startDate = startDate;
return this;
}
@Override
public TourPlanBuilder whereToStay(String whereToStay) {
this.whereToStay = whereToStay;
return this;
}
@Override
public TourPlanBuilder addPlan(int day, String plan) {
if (this.plans == null) {
this.plans = new ArrayList<>();
}
this.plans.add(new DetailPlan(day, plan));
return this;
}
@Override
public TourPlan getPlan() {
return new TourPlan(title, startDate, days, nights, whereToStay, plans);
}
}
return tourPlanBuilder.title("칸쿤 여행")
.nightsAndDays(2, 3)
.startDate(LocalDate.of(2020, 12, 9))
.whereToStay("리조트")
.addPlan(0, "체크인하고 짐 풀기")
.addPlan(0, "저녁 식사")
.getPlan();
//Director
public class TourDirector {
private TourPlanBuilder tourPlanBuilder;
public TourDirector(TourPlanBuilder tourPlanBuilder) {
this.tourPlanBuilder = tourPlanBuilder;
}
public TourPlan cancunTrip() {
return tourPlanBuilder.title("칸쿤 여행")
.nightsAndDays(2, 3)
.startDate(LocalDate.of(2020, 12, 9))
.whereToStay("리조트")
.addPlan(0, "체크인하고 짐 풀기")
.addPlan(0, "저녁 식사")
.getPlan();
}
public TourPlan longBeachTrip() {
return tourPlanBuilder.title("롱비치")
.startDate(LocalDate.of(2021, 7, 15))
.getPlan();
}
}
public static void main(String[] args) {
TourDirector director = new TourDirector(new DefaultTourBuilder());
TourPlan tourPlan = director.cancunTrip();
}
장점
- 만들기 복잡한 객체를 순차적으로 만들 수 있다.
- 복잡한 객체를 만드는 구체적인 과정을 숨길 수 있다.
- 동일한 프로세스를 통해 각기 다르게 구성된 객체를 만들 수도 있다.
- 불완전한 객체를 사용하지 못하도록 방지할 수 있다.
단점
- 원하는 객체를 만들려면 빌더부터 만들어야 한다.
- 구조가 복잡해 진다.(트레이드 오프)
728x90
'이론 > GoF' 카테고리의 다른 글
[구조패턴] 플라이웨이트 패턴 (0) | 2023.07.20 |
---|---|
[생성패턴] 프로토타입패턴 (0) | 2023.06.01 |
[생성패턴] 추상팩토리 패턴 (0) | 2023.05.22 |
[생성패턴] 팩토리 메소드 패턴 (0) | 2023.05.22 |
[생성패턴] 싱글톤 패턴 깨트리기 (0) | 2023.05.21 |