메뉴

문서정보

목차

빌더 패턴

객체를 만드는 디자인 패턴이다. factory 패턴과 abstract factory 패턴과 유사하다. 유사한데도 사용하는 이유가 있는데, factory, absstract factory 패턴은 아래와 같은 문제점이 있다.

해결 방법

  1. 불필요한 생성자를 만들지 않고 객체를 만들 수 있어야 한다.
  2. 파라메터의 순서와 상관없이 객체를 만들 수 있어야 한다.
  3. 파라메터의 단순 입력과 달리, 명시적이고 이해 할 수 있는 수단을 마련해야 한다.
헷갈리지 않게 명확히 객체를 만들 수 있게 하자는게 요지다.

간단 구현

자동차 객체를 생성하는 클래스를 설계해 보자. 자동차 객체를 만들기 위해서는 색상, 최고속도, 휠 타입 등 다양한 파라메터의 조정이 필요하다. 게다가 차종에 따라서 파라메터가 다를 수도 있다.
package main

import (
    "fmt"
    "strconv"
)

const (
    BLUE string = "blue"
    RED         = "red"
)

type car struct {
    Speed int
    Color string
}

func NewBuilder() *car {
    return &car{}
}

func (c *car) TopSpeed(speed int) *car {
    c.Speed = speed
    return c
}

func (c *car) Painting(color string) *car {
    c.Color = color
    return c
}

func (c *car) Drive() string {
    return "Driving at speed: " + strconv.Itoa(c.Speed)
}

func (c *car) Stop() string {
    return "Stopping a " + string(c.Color) + " car"
}

func main() {
    sportsCar := NewBuilder().Painting(BLUE).TopSpeed(250)
    fmt.Println(sportsCar.Drive())
    fmt.Println(sportsCar.Stop())

    famillyCar := NewBuilder().Painting(RED).TopSpeed(100)
    fmt.Println(famillyCar.Drive())
    fmt.Println(famillyCar.Stop())
}

	
		
간단한 구현이다. Chaining Method를 이용, 각 메서드에서 차량의 세부 정보를 설정 할 수 있도록 했다.

개선해보자.

위 프로그램을 좀 더 객체지향적으로 개선해보자. 차량의 제조 과정은 대략 아래와 같이 정의 할 수 있을 것이다.
  1. 차종에 따른 차량 옵션을 설정
  2. 옵션대로 차량을 만든다. 이 시점에서 Driving과 Stop 같은 기능이 작동한다.
package main

import (
    "fmt"
    "strconv"
)

type Color string
type Make string
type Model string

const (
    BLUE Color = "blue"
    RED        = "red"
)

type Car interface {
    Drive() string
    Stop() string
}

type CarBuilder interface {
    TopSpeed(int) CarBuilder
    Paint(Color) CarBuilder
    Build() Car
}

type carBuilder struct {
    speedOption int
    color       Color
}

func (cb *carBuilder) TopSpeed(speed int) CarBuilder {
    cb.speedOption = speed
    return cb
}

func (cb *carBuilder) Paint(color Color) CarBuilder {
    cb.color = color
    return cb
}

func (cb *carBuilder) Build() Car {
    return &car{
        topSpeed: cb.speedOption,
        color:    cb.color,
    }
}

func New() CarBuilder {
    return &carBuilder{}
}

type car struct {
    topSpeed int
    color    Color
}

func (c *car) Drive() string {
    return "Driving at speed: " + strconv.Itoa(c.topSpeed)
}

func (c *car) Stop() string {
    return "Stopping a " + string(c.color) + " car"
}

func main() {
    assembly := New()
    sportsCar := assembly.TopSpeed(250).Paint(BLUE).Build()
    famillyCar := assembly.TopSpeed(80).Paint(RED).Build()

    fmt.Println(sportsCar.Drive())
    fmt.Println(sportsCar.Stop())

    fmt.Println(famillyCar.Drive())
    fmt.Println(famillyCar.Stop())
}

		
차량 옵션과 기능을 설정하기 위해서 CarBuilder 인터페이스를 준비했다. TopSpeed, Paint 메서드를 이용해서 차량 옵션을 설정할 수 있다. 그리고 차량의 기능인 Drive와 Stop 역시 인터페이스로 구성하기로 했다. 이제 개발자는 TopSpeed와 Paint 메서드를 구현해서 옵션을 설정하고, Drive와 Stop의 구현으로 차량 기능을 만들 수 있다.

현실의 예

빌더패턴은 복잡한 객체의 구성과 표현을 분리해서, 동일한 구성의 프로세스로 다른 표현이 가능하게 한다. 이 패턴은 패스트 푸드 식당에서 아이들의 식사를 만드는데 사용 할 수 있다.

어린이 식사는 주 물품, 부대 아이템, 음료 및 장난감등으로 구성된다. 이때 식사의 구성에 차이가 있을 수는 있지만 동일한 제조 프로세스를 따른다는 것에 유의해야 한다. 고객이 치즈버거를 주문하든 불고기버거를 주문하든 만드는 과정은 동일하다.

 빌더패턴 현실 예제

참고