빌더 패턴

dev_hobin·2022년 4월 10일
0

디자인 패턴

목록 보기
2/2
post-thumbnail

빌더 패턴

  • 일괄된 프로세스로 다양한 구성의 인스턴스를 만드는 패턴

투어 일정 생성

  • 투어 일정을 생성하는 코드를 구현하며 패턴을 이해해보자
// 투어 일정
class TourPlan {
  #title: string | undefined;
  #nights: number | undefined;
  #days: number | undefined;
  #startDate: Date | undefined;
  #whereToStay: string | undefined;
  #plans: DetailPlan[] | undefined;

  get title() {
    return this.#title;
  }
  set title(v: string | undefined) {
    this.#title = v;
  }
  get nights() {
    return this.#nights;
  }
  set nights(v: number | undefined) {
    this.#nights = v;
  }
  get days() {
    return this.#days;
  }
  set days(v: number | undefined) {
    this.#days = v;
  }
  get startDate() {
    return this.#startDate;
  }
  set startDate(v: Date | undefined) {
    this.#startDate = v;
  }
  get whereToStay() {
    return this.#whereToStay;
  }
  set whereToStay(v: string | undefined) {
    this.#whereToStay = v;
  }
  get plans() {
    return this.#plans;
  }
  set plans(v: DetailPlan[] | undefined) {
    this.#plans = v;
  }
  addPlan(day: number, plan: string) {
    if (this.#plans) {
      this.#plans.push(new DetailPlan(day, plan));
    } else {
      this.#plans = [new DetailPlan(day, plan)];
    }
  }
}
// 투어 일정에 들어가는 세부 일정
class DetailPlan {
  #day: number;
  #plan: string;

  constructor(day: number, plan: string) {
    this.#day = day;
    this.#plan = plan;
  }

  get day() {
    return this.#day;
  }
  get plan() {
    return this.#plan;
  }
}
// 클라이언트 코드
const tourPlan = new TourPlan();
tourPlan.title = '칸쿤여행';
tourPlan.nights = 2;
tourPlan.days = 3;
tourPlan.startDate = new Date(2021, 3, 10);
tourPlan.whereToStay = '리조트';
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, '체크아웃');

const shortPlan = new TourPlan();
shortPlan.title = '오레곤 롱비치 여행';
shortPlan.startDate = new Date(2021, 4, 15);

코드의 문제점

  • tourPlan 과 shortPlan 을 만들었는데, 만들 때 일괄된 프로세스가 없다.
  • 투어 일정을 만들 때 속성을 설정하는 자유도가 너무 높아서 불완전한 객체가 만들어질 위험이 있다
    ex) 2박 3일 일정인데, nights 속성은 2 로 설정하고 days 속성은 설정하지 않은 경우
  • 투어 종류에 따라 필요한 속성들을 생성자를 통해서 강제하려고 할 경우, 생성자의 수가 장황하게 많아질 수 있다

빌더 패턴 적용

// 빌더 인터페이스 정의
interface TourPlanBuilder {
  // 속성 세팅 메서드
  title(title: string): TourPlanBuilder;
  nightsAndDays(nights: number, days: number): TourPlanBuilder; // 항상 함께 세팅되어야하는 속성들 묶을 수 있다
  startDate(date: Date): TourPlanBuilder;
  whereToStay(whereToStay: string): TourPlanBuilder;
  addPlan(day: number, plan: string): TourPlanBuilder;
  // 완성된 투어 일정 받는 메서드
  getPlan(): TourPlan;
}
// 빌더에서 쓸 생성자 추가
interface TourPlanConstructor {
  title?: string;
  nights?: number;
  days?: number;
  startDate?: Date;
  whereToStay?: string;
  plans?: DetailPlan[];
}
export class TourPlan {
  #title: string | undefined;
  #nights: number | undefined;
  #days: number | undefined;
  #startDate: Date | undefined;
  #whereToStay: string | undefined;
  #plans: DetailPlan[] | undefined;

  constructor(args?: TourPlanConstructor) {
    this.#title = args?.title;
    this.#nights = args?.nights;
    this.#days = args?.days;
    this.#startDate = args?.startDate;
    this.#whereToStay = args?.whereToStay;
    this.#plans = args?.plans;
  }
	// ... 코드 생략
}
// 빌더 인터페이스 구현체
class DefaultTourBuilder implements TourPlanBuilder {
  #title: string | undefined;
  #nights: number | undefined;
  #days: number | undefined;
  #startDate: Date | undefined;
  #whereToStay: string | undefined;
  #plans: DetailPlan[] | undefined;

  title(title: string): TourPlanBuilder {
    this.#title = title;
    return this;
  }
  nightsAndDays(nights: number, days: number): TourPlanBuilder {
    this.#nights = nights;
    this.#days = days;
    return this;
  }
  startDate(date: Date): TourPlanBuilder {
    this.#startDate = date;
    return this;
  }
  whereToStay(whereToStay: string): TourPlanBuilder {
    this.#whereToStay = whereToStay;
    return this;
  }
  addPlan(day: number, plan: string): TourPlanBuilder {
    if (this.#plans) {
      this.#plans.push(new DetailPlan(day, plan));
    } else {
      this.#plans = [new DetailPlan(day, plan)];
    }
    return this;
  }
  getPlan(): TourPlan {
    return new TourPlan({
      title: this.#title,
      nights: this.#nights,
      days: this.#days,
      startDate: this.#startDate,
      whereToStay: this.#whereToStay,
      plans: this.#plans,
    });
  }
}
// 클라이언트 코드
const cancunTrip: TourPlan = new DefaultTourBuilder()
  .title('칸쿤 여행')
  .nightsAndDays(2, 3)
  .startDate(new Date(2021, 3, 10))
  .whereToStay('리조트')
  .addPlan(0, '체크인 후 짐풀기')
  .addPlan(0, '저녁 식사')
  .addPlan(1, '조식 뷔페에서 식사')
  .addPlan(1, '해변가 산책')
  .addPlan(1, '점심은 수영장 근처 음식점에서 먹기')
  .addPlan(1, '리조트 수영장에서 놀기')
  .addPlan(1, '저녁은 BBQ 식당에서 스테이크')
  .addPlan(2, '조식 뷔페에서 식사')
  .addPlan(2, '체크아웃')
  .getPlan();

const longBeachTrip: TourPlan = new DefaultTourBuilder()
  .title('오레곤 롱비치 여행')
  .startDate(new Date(2021, 4, 15))
  .getPlan();
  • 빌더 패턴을 적용함으로써 메서드 체이닝을 통해 일괄된 프로세스를 만들었다
  • 함께 설정되어야 하는 속성들을 한 메서드를 통해 관리하므로써 불완전한 객체의 생성을 방지했다

한번 더 래핑

  • 자주 반복되어 만들어지는 투어 일정들은 한번 더 래핑할 수 있다.
class TourDirector {
  #tourPlanBuilder: TourPlanBuilder;

  constructor(tourPlanBuilder: TourPlanBuilder) {
    this.#tourPlanBuilder = tourPlanBuilder;
  }

  cancunTrip(): TourPlan {
    return this.#tourPlanBuilder
      .title('칸쿤 여행')
      .nightsAndDays(2, 3)
      .startDate(new Date(2021, 3, 10))
      .whereToStay('리조트')
      .addPlan(0, '체크인 후 짐풀기')
      .addPlan(0, '저녁 식사')
      .addPlan(1, '조식 뷔페에서 식사')
      .addPlan(1, '해변가 산책')
      .addPlan(1, '점심은 수영장 근처 음식점에서 먹기')
      .addPlan(1, '리조트 수영장에서 놀기')
      .addPlan(1, '저녁은 BBQ 식당에서 스테이크')
      .addPlan(2, '조식 뷔페에서 식사')
      .addPlan(2, '체크아웃')
      .getPlan();
  }

  longBeachTrip(): TourPlan {
    return this.#tourPlanBuilder
      .title('오레곤 롱비치 여행')
      .startDate(new Date(2021, 4, 15))
      .getPlan();
  }
}
// 클라이언트 코드
const director = new TourDirector(new DefaultTourBuilder());

const cancunTrip: TourPlan = director.cancunTrip();
const longBeachTrip: TourPlan = director.longBeachTrip();
profile
무엇을 기억할지 고민하는 것이 공부다

0개의 댓글