DEV ℧ Developer Diary

[DesignPattern] 빌더 (Builder)

빌더(Builder)

객체를 생성하는 방법과 표현하는 방법을 정의하는 클래스를 별도로 분리하여, 동일한 절차에서 서로 다른 표현으로 생성할 수 있도록 제공해줍니다. 이펙티브 자바 item02 에 나온 Builder와는 다르게 좀더 방법론적으로 설명 해준다고 생각해주면 된다.

차이점을 간단하게 구분해 주자면, 이펙티브 자바의 빌더패턴의 경우 생성자에 매게변수를 받아 Entity를 보호하는것에 있다면, GOF 디자인 패턴의 경우 추상화 팩터리 패턴과 비슷하지만 다르게, 객체지향의 관점에서 객체의 생성과 조립을 각각 분리하여 구현하는것을 말한다.

활용 방안

빌더 패턴은 다음의 경우에 사용할 수 있다.

  • 복합 객체의 생성 알고리즘이 이를 합성하는 요소 객체들이 무엇인지 이들의 조립 방법에 독립적일 때
  • 합성할 객체들의 표현이 서로 다르더라도 생성 절차에서 이를 지원해야 할 때

구조

빌더 구조

  • Builder : Product 객체의 일부 요소들을 생성하기 위한 추상 인터페이스를 정의합니다.
  • ConcreteBuilder : Builder 클래스에 정의된 인터페이스를 구현하며, 제품의 부품들을 모아 빌더를 복합합니다. 생성한 요소의 표헌을 정의하고 관리합니다. 또한 제품을 검색하는 데 필요한 인터페이스를 제공합니다.
  • Director : Builder 인터페이스를 사용하는 객체를 합성합니다.
  • Product : 생성할 복합 객체를 표현합니다. ConcreteBuilder는 제품(Product)의 내부 표현을 구축하고 복합 객체가 어떻게 구성되는지에 관한 절차를 정의합니다.

장점

  1. 제품에 대한 내부 표현을 다양하게 변화할 수 있습니다.
  2. 생성과 표현에 필요한 코드를 분리할 수 있습니다.
  3. 복합 객체를 생성하는 절차를 좀더 세밀하게 나눌 수 있습니다.

구현

빌터 구현에 설계한 디렉토리 구조는 아래와 같습니다.

.
├── builder
│   └── HamburgerBuilder
├── concretebuilder
│   ├── BurgerKingHamburgerBuilder
│   └── MomsTouchHamburgerBuilder
├── director
│   └── HamburgerStore
├── Product
│   └── Hamburger
└── Main

1. 생성할 복합 객체를 만듭니다.

생성하고자 하는 객체를 만듭니다.

아래에서는 햄버거 생성과정을 Builder로 표현했습니다.

/**
 * Product
 * 생성할 복합 객체를 표현합니다.
 * ConcreteBuilder는 제품(Product)의 내부 표현을 구축하고 복합 객체가 어떻게 구성되는지에 관한 절차를 정의합니다.
 */
public class Hamburger {
    public enum Patty {NONE, BEEF, CHICKEN};

    public Patty patty = Patty.NONE;

    public void setPatties(Patty patty) {
        this.patty = patty;
    }

    @Override
    public String toString() {
        return "patty = " + patty;
    }
}

2. 객체를 생성하는 메서드들 정의

HamburgerBuilder 클래스를 통해 햄버거를 생성하는데 필요한 인터페이스를 정의합니다.

/**
 * Builder
 * Product 객체의 일부 요소들을 생성하기 위한 추상 인터페이스를 정의합니다.
 */
public abstract class HamburgerBuilder {
    protected Hamburger hamburger;

    public Hamburger createHamburger() {
        return hamburger;
    }

    public void prepareNewHamburger() {
        hamburger = new Hamburger();
    }

    public abstract void setHamburger();
}

3. 구현할 객체의 서브클래스를 정의합니다.

HamburgerBuilder를 상속받아 어떤 종류의 햄버거를 만들지 서브클래스를 정의해줍니다.

/**
 * ConcreteBuilder1
 * Builder 클래스에 정의된 인터페이스를 구현하며, 제품의 부품들을 모아 빌더를 복합합니다.
 * 생성한 요소의 표헌을 정의하고 관리합니다. 또한 제품을 검색하는 데 필요한 인터페이스를 제공합니다.
 */
public class BurgerKingHamburgerBuilder extends HamburgerBuilder {

    @Override
    public void setHamburger() {
        hamburger.setPatties(Hamburger.Patty.BEEF);
    }
}

/**
 * ConcreteBuilder2
 * Builder 클래스에 정의된 인터페이스를 구현하며, 제품의 부품들을 모아 빌더를 복합합니다.
 * 생성한 요소의 표헌을 정의하고 관리합니다. 또한 제품을 검색하는 데 필요한 인터페이스를 제공합니다.
 */
public class MomsTouchHamburgerBuilder extends HamburgerBuilder {

    @Override
    public void setHamburger() {
        hamburger.setPatties(Hamburger.Patty.CHICKEN);
    }
}

4. 생성한 객체를 출력합니다.

public class Main {
    public static void main(String[] args) {
        HamburgerStore store = new HamburgerStore();
        MomsTouchHamburgerBuilder momsTouchBuilder = new MomsTouchHamburgerBuilder();
        BurgerKingHamburgerBuilder burgerKingBuilder = new BurgerKingHamburgerBuilder();

        /* 맘스터치 햄버거 생성 */
        store.setHamburgerBuilder(momsTouchBuilder);
        store.makeHamburger();
        Hamburger momsTouch = store.getHamburger();
        System.out.println("MomsTouch : " + momsTouch.toString());

        /* 버거킹 햄버거 생성 */
        store.setHamburgerBuilder(burgerKingBuilder);
        store.makeHamburger();
        Hamburger burgerKing = store.getHamburger();
        System.out.println("BurgerKing : " + burgerKing.toString());
    }
}

출력된 결과

MomsTouch : patty = CHICKEN
BurgerKing : patty = BEEF

정리

다시한번 말하자면 이펙티브 자바에서 구현된 빌더와는 다른 방법론적의 빌더 패턴이라 추상적 팩토리 패턴에 가깝다.

객체의 생성과 표현을 분리하여 구현한다고 보면된다.

예제 코드

빌더 Github 전체 소스