빌더(Builder)

의도

복잡한 객체를 생성하는 방법과 표현하는 방법을 정의하는 클래스를 별도로 분리하여, 서로 다른 표현이라도 이를 생성할 수 있는 동일한 절차를 제공할 수 있도록 한다.

활용성

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

구조

Image Alt 텍스트

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

협력 방법

  • 사용자는 Director 객체를 생성하고, 이렇게 생성한 객체를 자신이 원하는 Builder 객체로 합성해 나간다.
  • 제품의 일부가 구축(built)될 때마다 Director는 Builder에 통보한다.
  • Builder는 Director의 요청을 처리하여 제품에 부품을 추가한다.
  • 사용자는 Builder에서 제품을 검색한다.

결과

장점

  1. 제품에 대한 내부 표현을 다양하게 변화할 수 있다.
    • Builder 객체는 디렉터를 제공하고 제품을 복합하기 위해 필요한 추상 인터페이스를 정의한다.
      즉, 어떤 요소로 전체 제품을 복합하고 그 요소들이 어떤 타입들로 구현되는지 알고 있는 쪽은 빌더 뿐이다.
      • 제품을 복합할 때는 빌더에 정의한 추상 인터페이스를 통해 사용자가 동작하기 때문에, 새로운 제품의 표현 방법이나 제품의 복합 방법이 바뀔 떄 추상 인터페이스를 정의한 Builder 클래스에서 상속을 통해 새로운 서브클래스를 정의하면 된다.
  2. 생성과 표현에 필요한 코드를 분리한다.
    • 빌더 패턴을 사용하면, 복합 객체를 생성하고 복합 객체의 내부 표현 방법을 별도의 모듈로 정의할 수 있다.
      사용자는 제품의 내부 구조를 정의한 클래스는 전혀 모른 채(모듈화), 빌더와 상호작용을 통해서 필요한 복합 객체를 생성한다.
  3. 복합 객체를 생성하는 절차를 좀더 세밀하게 나눌 수 있다.
    • 한번에 복합 객체를 생성하는 것처럼, 빌더 패턴은 디렉터의 통제 아래 하나씩 내부 구성요소들을 만들어 나간다.
      그렇기 떄문에 Builder 클래스의 인터페이스에는 이 제품을 생성하는 과정 자체가 반영되어 있다.

구현

  1. 추상 클래스인 Builder 클래스에 디렉터가 요청하는 각각의 요소들을 생성하는 연산들을 정의한다.
    • 기본적으로 여기 정의된 연산은 아무것도 구현되지 않은 단순한 인터페이스이다.
      • 이 클래스를 상속하는 서브클래스 ConcreteBuilder가 자신에게 필요한 요소를 생성하도록 재정의한다.
  2. 조합과 구축에 필요한 인터페이스를 정의한다.
    • 빌더는 단계별로 제품들을 생성한다. 이를 위해서 모든 종류의 제품을 생성하는 데 필요한 일반화된 연산들을 정의한다.

예제코드

  • Product
class Pizza {
	private String dough = "";
	private String sauce = "";
	private String topping = "";

	// setter
}
  • Abstract Builder
abstract class PizzaBuilder {
	protected Pizza pizza;
	
	public Pizza getPizza() {
		return pizza;
	}
	
	public void creaateNewPizzaProduct() {
		pizza = new Pizza();
	}

	public abstract void buildDough();
	public abstract void buildSauce();
	public abstract void buildTopping();
	
}
  • ConcreteBuilder
class HawaiianPizzaBuilder extends PizzaBuilder {
	public void buildDough() {
		pizza.setDough("cross");
	}

	public void buildSauce() {
		pizza.setSauce("mild");
	}

	public void buildTopping() {
		pizza.setTopping("ham+pineapple");
	}
}


class SpicyPizzaBuilder extends PizzaBuilder {
	public void buildDough() {
		pizza.setDough("pan baked");
	}

	public void buildSauce() {
		pizza.setSauce("hot");
	}

	public void buildTopping() {
		pizza.setTopping("pepperoni+salami");
	}
}
  • Director
class Cook {
	private PizzaBuilder pizzaBuilder;
	
	public void setPizzaBuilder(PizzaBuilder pizzaBuilder) {
		this.pizzaBuilder = pizzaBuilder;
	}

	public Pizza getPizza() {
		return pizzaBuilder.getPizza();
	}
	
	public void constructPizza() {
		pizzaBuilder.createNewPizzaProduct();
		pizzaBuilder.buildDough();
		pizzaBuilder.buildSauce();
		pizzaBuilder.buildTopping();
	}
}
  • 생성할 피자 타입
public class BuilderExample {
	public static void main(String[] args){
	  Cook cook = new Cook();
	  PizzaBuilder hawaiianPizzaBuilder = new HawaiianPizzaBuilder();
	  PizzaBuilder spicyPizzaBuilder = new SpicyPizzaBuilder();
	  
	  cook.setPizzaBuilder(hawaiianPizzaBuilder);
	  cook.constructPizza();
	  
	  Pizza pizza = cook.getPizza();
	}
}

관련 패턴

복잡한 객체를 생성해야 할 때 추상 팩토리 패턴빌더 패턴은 비슷한 모습을 보이지만, 다음과 같은 차이점이 있다.

  • 빌더 패턴
    1. 복잡한 객체의 단계별 생성에 중점
    2. 생성의 마지막 단계에서 생성한 제품을 반환
  • 추상 팩토리 패턴
    1. 제품의 유사군들이 존재할 때 유연한 설계에 중점
    2. 만드는 즉시 제품을 반환
      • 추상 팩토리 패턴에서 만드는 제품은 꼭 모여야만 의미있는 것이 아니라, 하나만으로도 의미가 있음