본문 바로가기

생성 디자인 패턴 - 빌더 본문

OOP 디자인 패턴/생성패턴

생성 디자인 패턴 - 빌더

개발자로 거듭나기 2023. 5. 18. 10:27
반응형

빌더 패턴 (Builder)

  • 빌더는 복잡한 객체들을 단계별로 생성할 수 있도록 하는 생성 디자인 패턴입니다. 이 패턴을 사용하면 같은 제작 코드를 사용하여 객체의 다양한 유형들과 표현을 제작할 수 있습니다.
  • 빌더 패턴을 사용하는 것은 제품에 매우 복잡하고 광범위한 설정이 필요한 경우에만 의미가 있습니다.

점층적 생성자의 제거

class Pizza {
    Pizza(int size) { ... }
    Pizza(int size, boolean cheese) { ... }
    Pizza(int size, boolean cheese, boolean pepperoni) { ... }
    // …
  • 위의 코드는 여러개의 생성자를 오버로딩하여 객체 생성시, 입맛에 맞는 피자를 만드는 코드입니다.
  • 이렇게 생성자를 오버로딩하면 생성자가 길고 복잡해집니다. 이런 상황에서 빌더패턴을 적용해 볼 수 있습니다.
  • 빌더패턴은 이러한 피자 클래스로부터 생성자(생성)코드를 추출하여 빌더클래스로 옮기라고 제안합니다.

빌더 구성요소

  1. 빌더 인터페이스 : 객체(제품)를 만드는데 필요한 과정들을 미리 정의합니다.
  2. 구상 빌더 클래스 : 특정 제품들을 만드는데 필요한 과정을 구현합니다.
  3. 디렉터 클래스 : 빌더 클래스와 상호작용하여서 특정 제품(특정 과정생략 또는 추가)을 만듭니다.
  4. 클라이언트 코드 with director : 디렉터 클래스가 사전에 정의해 놓은 제품들을 생산하는 메서드를 호출하여 간결한 코드로 제품들을 생산합니다.
  5. 클라이언트 코드 without director : 빌더 클래스에 있는 제품들을 만드는 과정들을 호출하여 제품들을 생산합니다.

요약

  • 빌더는 생성패턴이며, 객체를 생성하는데 사용자의 입맛에 맞게 순서나 과정을 조작하여 객체를 만들 수 있습니다.
  • 디렉터는 필수요소는 아닙니다. 필요에 따라 선언하고 사용할 수 있고, 간편하게 제품을 만들 수 있다는 장점이 있습니다.
  • 디렉터를 사용하지 않는 빌더패턴은 과정의 추가나 순서의 변경을 유동적으로 할 수 있어 수공업의 느낌입니다.
  • 디렉터를 사용하면 클라이언트는 디렉터가 정의해놓은 순서나 과정으로 항상 같은 제품을 얻을 수 있기 때문에 공장의 느낌이 납니다.
  • 물론 디렉터에서도 빌더를 이용해서 디렉터를 사용하지 않을 때 처럼 조립할 수 있습니다.
반응형

코드

interface PizzaBuilder {
  baconTopping(): void;
  broccoliTopping(): void;
  shrimpTopping(): void;
}

class SweetPotatoPizzaBuilder implements PizzaBuilder {
  private pizza: SweetPotatoPizza;

  constructor() {
    this.reset();
  }

  public reset(): void {
    this.pizza = new SweetPotatoPizza();
  }

  public baconTopping(): void {
    this.pizza.toppings.push('bacon');
  }

  public broccoliTopping(): void {
    this.pizza.toppings.push('broccoli');
  }

  public shrimpTopping(): void {
    this.pizza.toppings.push('shrimp');
  }

  public getProduct(): SweetPotatoPizza {
    const result = this.pizza;
    // 리셋을 호출하면 제품을 다 만들고 나서 새로운 피자를 만들 수 있게 됩니다. (초기화)
    // 그러나 이것은 선택사항이고, 제품을 다 만들고 reset을 호출하지 않고 제품의 결과를 갖고 있을 수 있습니다.
    this.reset();
    return result;
  }
}

class SweetPotatoPizza {
  public toppings: string[] = [];

  // 각각 제품들의 특징을 보여주기
  public listToppings(): void {
    console.log(`toppings : ${this.toppings.join(', ')}\n`);
  }
}

class PizzaDirector {
  private builder: PizzaBuilder;

  // 빌더 하나와 상호작용 하면서 제품 생산
  public setBuilder(builder: PizzaBuilder): void {
    this.builder = builder;
  }

  // 베이컨 토핑만 추가하는 피자 제작
  public onlyBaconTopping(): void {
    this.builder.baconTopping();
  }

  // 모든 토핑이 들어간 피자 제작
  public allToppings(): void {
    this.builder.baconTopping();
    this.builder.broccoliTopping();
    this.builder.shrimpTopping();
  }
}

function 피자디렉터이용(director: PizzaDirector) {
  const pizzaBuilder = new SweetPotatoPizzaBuilder();
  director.setBuilder(pizzaBuilder);

  console.log('베이컨 토핑');
  director.onlyBaconTopping();
  pizzaBuilder.getProduct().listToppings();

  console.log('모든토핑 첨가');
  director.allToppings();
  pizzaBuilder.getProduct().listToppings();

  console.log('브로콜리 피자');
  pizzaBuilder.broccoliTopping();
  pizzaBuilder.getProduct().listToppings();
}
const pizzaDirector = new PizzaDirector();
피자디렉터이용(pizzaDirector);

const 브로콜리빼주세요 = () => {
  const pizzaBuilder = new SweetPotatoPizzaBuilder();

  console.log('(커스텀 피자 - 빌더로 조립) custom pizza : ');
  pizzaBuilder.baconTopping();
  pizzaBuilder.shrimpTopping();
  pizzaBuilder.getProduct().listToppings();
}
브로콜리빼주세요();

결과

출처

https://refactoring.guru/ko/design-patterns/builder

반응형
Comments