개발자로 후회없는 삶 살기
디자인 패턴 PART.빌더 패턴 본문
서론
※ 이 포스트는 다음 강의의 학습이 목표임을 밝힙니다.
https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4/dashboard
본론
- 빌더 패턴 소개
어떤 객체를 만들 때 객체가 다양한 구성으로 만들어질 수 있는데 동일한 프로세스를 통해서 만들 수 있게 해주는 패턴입니다.
=> 코드
public class App {
public static void main(String[] args) {
TourPlan tourPlan = new TourPlan();
tourPlan.setTitle("칸쿤 여행");
tourPlan.setNights(2);
tourPlan.setDays(3);
tourPlan.setStartDate(LocalDate.of(2020, 12, 9));
tourPlan.setWhereToStay("리조트");
tourPlan.addPlan(0, "체크인 이후 짐풀기");
tourPlan.addPlan(0, "저녁 식사");
tourPlan.addPlan(1, "조식 부페에서 식사");
tourPlan.addPlan(1, "해변가 산책");
tourPlan.addPlan(1, "점심은 수영장 근처 음식점에서 먹기");
tourPlan.addPlan(1, "리조트 수영장에서 놀기");
tourPlan.addPlan(1, "저녁은 BBQ 식당에서 스테이크");
tourPlan.addPlan(2, "조식 부페에서 식사");
tourPlan.addPlan(2, "체크아웃");
}
}
여행 상품 TourPlan 객체를 만드는 클라이언트 코드입니다.
public class TourPlan {
private String title;
private int nights;
private int days;
private LocalDate startDate;
private String whereToStay;
private List<DetailPlan> plans;
TourPlan 코드는 이름, 몇 박, 시작 날, 구체적인 계획들을 필드로 가지고 있으며
public class DetailPlan {
private int day;
private String plan;
구체적인 계획에 해당하는 객체를 가지고 있을 때 간단하게 칸쿤으로 가는 2박 3일 여행 계획을 만들 수 있습니다.
-> 짧은 여행이라면? ✅
TourPlan shortTrip = new TourPlan();
shortTrip.setTitle("오레곤 롱비치 여행");
shortTrip.setStartDate(LocalDate.of(2021, 7, 15));
shortTrip을 만들고 여행 이름, 시작날짜 등 짧지만 역시 계획을 만들 수 있습니다.
-> 설명
하지만 이 둘을 보면 계획을 짜는데 장황하고 일관된 프로세스가 없다는 것을 알 수 있고 박을 쓰면 일을 강제해야하는 것도 못 해서 객체가 불안정한 모습입니다.
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;
}
public TourPlan(String title) {
this.title = title;
}
또한 생성자를 만들 때 짧은 여행이면 title만 있으면 되고 긴 여행이면 장소도 있어햐 하는 등 생성자도 다 다른 모양으로 많아지게 됩니다.
이때 빌더 패턴을 적용하면 빌더에 객체를 만드는 방법을 인터페이스 안에 스탭별(A, B)로 만들어서 최종적으로 객체를 만들 수 있는 getProduct라는 메서드를 만들어서 구현체를 만들도록 합니다.
이렇게 구현체와 인터페이스의 관계라서 우리가 원한다면 인스턴스를 만드는 방법을 또 다른 구체 빌더를 만들어서 다양하게 객체를 만드는 빌더를 구성할 수 있습니다.
클라이언트가 구체 빌더를 사용해도 되지만 디렉터를 둬서 클라이언트가 디렉터를 사용하고 디렉터가 빌더를 사용하도록 하면 디렉터 안에 반복되는 빌더 호출 스택을 숨겨 놓을 수 있고 클라이언트는 디렉터를 통해서 빌더를 간단하게 사용할 수 있습니다.
- 패턴 적용하기
이전 코드를 빌더 패턴으로 바꿔보도록 하겠습니다.
1. 빌더 인터페이스
인터페이스에 어떠한 단계를 거쳐서 최종적인 tourPlan이라는 객체를 만들지를 정의합니다.
public interface TourPlanBuilder{
TourPlanBuilder title(String title);
}
TourPlan 빌더 타입을 반환하는 메서드를 먼저 만드는데 이를 통해서 메서드 체이닝을 할 수 있습니다. 예를들어 클라이언트에서 title을 호출하게 되면 TourPlan 빌더를 받게 되어서 TourPlan 빌더의 메서드를 사용할 수 있습니다.
public interface TourPlanBuilder{
TourPlanBuilder title(String title);
TourPlanBuilder nightsAndDays(int nights, int days);
TourPlan getPlan();
}
title을 쓴 후 nightsAndDays를 바로 연달아서 쓸 수 있습니다. 마지막으로는 TourPlan getPlan()을 호출하면 최종적으로 객체를 리턴 받을 수 있습니다.
- 구현 빌더
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 TourPlan getPlan() {
return new TourPlan(title, nights, days, startDate, whereToStay, plans);
}
최종적으로 TourPlan을 리턴하도록 할 것입니다.
- 클라이언트
public class App {
public static void main(String[] args) {
TourPlanBuilder builder = new DefaultTourBuilder();
TourPlan plan = builder.title("칸쿤 여행")
.nightsAndDays(2, 3)
.startDate(LocalDate.of(2022, 12, 3))
.whereToStay("리조트")
.addPlan(0, "체크인")
.addPlan(0, "바베큐")
.getPlan();
}
}
그러면 이제 클라이언트가 바로 사용할 수 있습니다. 빌더 인터페이스를 선언하고 구현 빌더를 생성하고 빌더의 메서드를 원하는 대로 체이닝한 후 get하면 TourPlan이 생성됩니다.
builder.title("롱비치")
.startDate(LocalDate.of(2021, 7, 15))
.getPlan();
shortTrip은 원하는 메서드만 선택해서 만들 수 있습니다. 우리가 이걸 생성자로 만들었으면 생성자가 장황해지고 생성자의 파라미터로 수많은 null 값을 넣어야 했을 것입니다.
-> 디렉터
이렇게 객체를 만드는 프로세스가 자주 반복이 된다면 이런 세트를 디렉터에 만들어 놓고 재사용할 수도 있습니다.
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(2022, 12, 3))
.whereToStay("리조트")
.addPlan(0, "체크인")
.addPlan(0, "바베큐")
.getPlan();
}
}
TourDirector 클래스를 만들고 생성자에서 빌더를 주입받은 후 cancunTrip을 아예 세트로 메서드로 만들어 버립니다.
public class App {
public static void main(String[] args) {
TourDirector director = new TourDirector(new DefaultTourBuilder());
TourPlan tourPlan = director.cancunTrip();
}
}
이렇게 하면 클라이언트는 빌더를 직접 건드리지 않고 정형화된 디렉터를 통해서 여행 계획 객체를 만들 수 있습니다.
- 장, 단점
1. 장점
1) 만들기 복잡한 객체를 순차적으로 만들 수 있습니다. 객체를 만드는 것을 규격화 하는 것입니다. 객체 내부에 생성자를 장황하게 만들 필요가 없어집니다.
2) 디렉터를 통해서 만드는 과정을 숨길 수 있습니다.
3) 동일한 프로세스를 통해서 다른 객체를 만들 수 있습니다. VIP 투어 빌더를 기획하고 동일한 프로세스를 거치지만 다른 객체가 나오게 할 수 있습니다.
2. 단점
객체를 만드는데 반드시 빌더나 디렉터를 만들어야 합니다.
'[백엔드] > [Java | 학습기록]' 카테고리의 다른 글
디자인 패턴 PART.어댑터 패턴 (0) | 2023.08.18 |
---|---|
디자인 패턴 PART.프로토타입 패턴 (0) | 2023.08.17 |
디자인 패턴 PART.추상 팩토리 패턴 (0) | 2023.08.13 |
디자인 패턴 PART.팩토리 메서드 패턴 (0) | 2023.08.13 |
디자인 패턴 PART.싱글톤 패턴 (0) | 2023.07.25 |