Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

빌더 패턴

객체를 만드는 디자인 패턴이다. factory 패턴과 abstract factory 패턴과 유사하다. 유사한데도 사용하는 이유가 있는데, factory, absstract factory 패턴은 아래와 같은 문제점이 있다.
  • 수맣은 파라메터들이 클래스로 전달되는데, 타입까지 비슷한 경우가 많기 때문에 파라메터를 제대로 사용하기가 쉽지 않아서 에러를 발생하는 경우가 많다.
  • 어떤 파라메터는 선택적이다. 팩토리패턴에서는 모든 파라메터를 전달해야 한다. 결국 null이나 0 같은 값을 넣어야 하는데, 코드를 혼란스럽게 한다.
  • 오브젝트가 무거울 수록 파라메터가 많아지고 위의 문제들이 가시화 된다.

해결 방법

  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의 구현으로 차량 기능을 만들 수 있다.

현실의 예

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

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

 빌더패턴 현실 예제

참고