Dog foot print

[GOF] Builder 패턴 본문

Architecture

[GOF] Builder 패턴

개 발자국 2021. 10. 23. 16:55

빌더 패턴

활용

구축 단계만 노출하여, 내부 구성요소를 캡슐화 하고 최종 제품을 직접 제공하는 패턴이다. 복잡한 객체 구축에 대한 더 유연한 추상화와 구현을 가능하게 한다. 즉 빌더는 구성요소를 만들 때, return 하지 않는다.

참여 객체

  1. 빌더 : 제품을 구축하는 빌더의 인터페이스를 정의한다.
  2. 구체적인 빌더 : 제품의 부분을 구축하는 메서드를 구현하며, 현재 구축 상태를 추적한다.
  3. 디렉터 : 단계를 정의하고 제품 구축을 위해 빌더와 협업한다.
  4. 최종 제품 : 빌더가 구축한 제품이다.

[image:1A0310E4-6A98-4677-B66E-2F97E85EC6BD-75143-0008E9D425CBAA97/2021-10-23_15-22-44.png]

코드 예시

내가 만들고 싶은 Product

abstract class Car {
    turnOn() {
        console.log("시동")
    }
    go() {
        console.log("출발")
    }
}

class Genesis extends Car {
    engine: string
    door: number
    wheel: number

    boost() {
        console.log("부스트 발동!")
    }
}

class Stinger extends Car {
    engine: string
    door: number
    wheel: number

    sportsModeOn() {
        console.log("스포츠 모드 On")
    }
}

Product를 만드는 builder 및 Interface

interface CarBuilder<T extends Car> {
    buildingCar: T

    prepareCar: () => void
    prepareDoor: () => void
    prepareWheel: () => void
    prepareEngine: () => void
}

class GenesisBuilder implements CarBuilder<Genesis> {

    buildingCar: Genesis

    prepareCar = () => {
        this.buildingCar = new Genesis()
    }

    prepareDoor = () => {
        this.buildingCar.door = 4
    }

    prepareWheel = () => {
        this.buildingCar.wheel = 4
    }

    prepareEngine = () => {
        this.buildingCar.engine = "현대 엔진 생성"
    }
}

class StingerBuilder implements CarBuilder<Stinger>{
    buildingCar: Stinger

    prepareCar = () => {
        this.buildingCar = new Stinger()
    }

    prepareDoor = () => {
        this.buildingCar.door = 2
    }

    prepareWheel = () => {
        this.buildingCar.wheel = 4
    }

    prepareEngine = () => {
        this.buildingCar.engine = "기아 엔진 생성"
    }
}

프로덕트를 만드는 빌더를 관장하는 디렉터

class CarBuildingDirector {

    prepareCar<TCar extends Car>(builder: CarBuilder<TCar>): TCar {
        builder.prepareCar()
        builder.prepareDoor()
        builder.prepareWheel()
        builder.prepareEngine()
        return builder.buildingCar
    }
}

실행 코드

const director = new CarBuildingDirector()

const genesis = director.prepareCar(new GenesisBuilder())
genesis.boost()

const stinger = director.prepareCar(new StingerBuilder())
stinger.sportsModeOn()

메서드 체인을 이용한 확장

얼핏 보면 추상화 팩토리 메서드와 다른점은 거의 없어 보인다. 눈에 띄게 보이는 점이라면 프로덕트를 생성하는 과정에서 모든 구성요소들을 정의하는 함수들은 구성요소들을 return 하지 않는 다는 것이다.

사실 위의 코드에서 director 는 필수요소가 아니다. 그렇다면 위의 director가 존재하지 않는다면 내가 원하는 대로 product를 꾸밀 수 있다는 장점이 발생한다.

CarBuilder Interface 변경

interface CarBuilder<T extends Car> {
    buildingCar: T

    prepareCar: () => this
    prepareDoor: (option: number) => this
    prepareWheel: () => this
    prepareEngine: (option: string) => this
    getCar: () => Car
}

Interface에 맞게 Builder 내부 구현 변경

class GenesisBuilder implements CarBuilder<Genesis> {

    buildingCar: Genesis

    prepareCar = () => {
        this.buildingCar = new Genesis()
        return this
    }

    prepareDoor = (option = 4) => {
        this.buildingCar.door = option
        return this
    }

    prepareWheel = () => {
        this.buildingCar.wheel = 4
        return this
    }

    prepareEngine = (option = "현대 엔진 생성") => {
        this.buildingCar.engine = option
        return this
    }

    getCar = () => {
        return this.buildingCar
    }
}

class StingerBuilder implements CarBuilder<Stinger>{
    buildingCar: Stinger

    prepareCar = () => {
        this.buildingCar = new Stinger()
        return this
    }

    prepareDoor = (option = 2) => {
        this.buildingCar.door = 2
        return this
    }

    prepareWheel = () => {
        this.buildingCar.wheel = 4
        return this
    }

    prepareEngine = (option = "기아 엔진 생성") => {
        this.buildingCar.engine = option
        return this
    }

    getCar = () => {
        return this.buildingCar
    }
}

Director 내부 구현 변경


class CarBuildingDirector {

    prepareCar<TCar extends Car>(builder: CarBuilder<TCar>): TCar {
        builder.prepareCar()
        builder.prepareDoor(null)
        builder.prepareWheel()
        builder.prepareEngine(null)
        return builder.buildingCar
    }
}

메서드 체인을 직접 사용하여, 인스턴스 활용

const tuningCar = new StingerBuilder()
    .prepareCar()
    .prepareDoor(4)
    .prepareWheel()
    .prepareEngine("튜닝 엔진")
    .getCar()

결론

빌더 패턴을 활용하면, 결과물로 원하는 인스턴스를 유연하게 생성가능하다. 추상 팩토리 메서드 혹은 팩토리 메서드를 활용하면, 객체를 유연하게 생성하기 위해서 많은 코드가 필요하다. 하지만 빌더 패턴을 활용하여, 유연하게 객체를 생성하기 위해서는 이 빌더를 사용하고 있는 개발자가 객체활용에 필요한 내용을 숙지 하지 못하고 엉뚱한 사이드 이슈를 가질 수 있는 단점이 존재한다.

반응형

'Architecture' 카테고리의 다른 글

[GOF] 컴포지트 패턴  (0) 2021.10.31
[GOF] 싱글톤 패턴  (0) 2021.10.31
[GOF] 추상 팩토리 메서드 패턴  (0) 2021.10.23
[GOF] 팩토리 메서드 패턴  (0) 2021.10.17
[Architecture] S.O.L.I.D [D]  (0) 2021.05.13
Comments