Typescript
빌더 패턴
bkdragon
2024. 11. 10. 23:09
빌더 패턴은 복잡한 객체를 생성하는 클래스와 표현하는 클래스를 분리하여, 동일한 절차에서도 서로 다른 표현을 생성하는 방법을 제공한다.
쉽게 말해 객체를 여러 단계에 걸처 만들 수 있다.
구성 요소
- Director(디렉터): Builder를 사용하여 객체를 생성하는 클래스. 추상 팩토리 패턴의 Client와 비슷한 역할을 한다.
- Builder(빌더): 객체의 각 부분을 만드는 방법을 정의하는 인터페이스
- ConcreteBuilder(구체적인 빌더): Builder 인터페이스를 구현하여 실제 객체를 만드는 클래스
- Product(제품): Builder를 통해 만들어지는 최종 객체
예시 코드
피자를 예로 들건데, 현실에서도 피자를 주문할 떄 크기를 선택하거나 토핑을 추가하거나 도우를 선택하는 등의 절차가 있을 수 있다.
// 제품
class Pizza {
private toppings: string[] = [];
private size: string = "";
private crustType: string = "";
public setSize(size: string): void {
this.size = size;
}
public setCrustType(type: string): void {
this.crustType = type;
}
public addTopping(topping: string): void {
this.toppings.push(topping);
}
public describe(): string {
return `${this.size} 사이즈 ${this.crustType} 피자 (토핑: ${this.toppings.join(", ")})`;
}
}
// Builder: 피자를 만드는 인터페이스
abstract class PizzaBuilder<TPizza extends Pizza> {
reset(): void {}
setSize(size: string): void {}
setCrustType(type: string): void {}
addTopping(topping: string): void {}
abstract getPizza(): TPizza;
}
// ConcreteBuilder: 실제 피자를 만드는 클래스
class CustomPizzaBuilder implements PizzaBuilder<Pizza> {
private pizza: Pizza;
constructor() {
this.pizza = new Pizza();
}
public reset(): void {
this.pizza = new Pizza();
}
public setSize(size: string): void {
this.pizza.setSize(size);
}
public setCrustType(type: string): void {
this.pizza.setCrustType(type);
}
public addTopping(topping: string): void {
this.pizza.addTopping(topping);
}
public getPizza(): Pizza {
const result = this.pizza;
this.reset();
return result;
}
}
// 디렉터: 빌더를 사용하여 객체를 생성하는 클래스
class Director {
preparePizza<TPizza extends Pizza>(
builder: PizzaBuilder<TPizza>,
option: {
size: string;
crustType: string;
toppings?: string[];
}
): TPizza {
builder.reset();
builder.setSize(option.size);
builder.setCrustType(option.crustType);
if (option.toppings) {
option.toppings.forEach((topping) => {
builder.addTopping(topping);
});
}
return builder.getPizza();
}
}
// 사용 예시
const builder = new CustomPizzaBuilder();
const director = new Director();
// 하와이안 피자 만들기
const hawaiianPizza = director.preparePizza(builder, {
size: "라지",
crustType: "씬",
toppings: ["파인애플", "햄", "모짜렐라 치즈"],
});
console.log(hawaiianPizza.describe());
// 페퍼로니 피자 만들기
const pepperoniPizza = director.preparePizza(builder, {
size: "미디움",
crustType: "치즈크러스트",
toppings: ["페퍼로니", "모짜렐라 치즈"],
});
console.log(pepperoniPizza.describe());
// 토핑 없는 피자 만들기
CustomPizzaBuilder 는 기본적인 피자를 만드는 빌더이다.
만약 토핑이 없는 피자를 만들고 싶다면, 새로운 빌더를 만들면 된다.
// 토핑 없는 피자 빌더 : 토핑 없는 피자를 만드는 구체적인 빌더
class NoToppingPizzaBuilder extends PizzaBuilder<Pizza> {
private pizza: Pizza;
reset(): void {
this.pizza = new Pizza();
}
setSize(size: string): void {
this.pizza.setSize(size);
}
setCrustType(type: string): void {
this.pizza.setCrustType(type);
}
// addTopping 메서드는 없음
getPizza(): Pizza {
return this.pizza;
}
}
const noToppingPizza = director.preparePizza(new NoToppingPizzaBuilder(), {
size: "라지",
crustType: "씬",
});
console.log(noToppingPizza.describe());
NoToppingPizzaBuilder 에서 addTopping 메서드를 구현하지 않았기 때문에 토핑 없는 피자만 만들 수 있다.
정리
빌더 패턴은 객체의 생성 과정을 여러 단계로 나누어 구성한다. 이를 통해 동일한 생성 과정을 가진 객체를 다양한 방법으로 생성할 수 있다. 특성 속성이 사용되지 않는 경우에도 유연하게 대처할 수 있다.