Search

Builder 패턴

패턴
생성

정의

생성패턴의 일종
복잡한 객체를 생성하는 과정과 표현 방법을 분리하여 동일한 생성 과정에서 다른 표현 결과(다양한 구성의 인스턴스)를 만들 수 있게 하는 디자인 패턴
객체 생성 과정을 단계별로 수행할 수 있으며, 각 단계별로 필요한 값을 설정하여 최종 객체를 생성
객체의 생성과 조립을 캡슐화하여 객체가 생성되는 방식과 생성된 객체의 표현 방식을 독립적으로 처리할 수 있게 도와준다.
복잡한 객체를 생성해야 하고, 이 객체의 구성 요소와 생성 과정이 사용자에게 노출되어서는 안 될 때 사용한다.
생성할 객체의 표현 방식이 서로 다른 변형 버전이 있을 때 사용한다.
생성된 객체의 일부 속성을 변경하거나 새로운 속성을 추가할 수 있다.

주요 구성 요소

1.
Builder : 객체의 생성과 조립 방법을 정의하는 인터페이스나 추상 클래스
각 단계별로 필요한 메서드를 선언
2.
Concrete Builder : Builder 인터페이스를 구현하여 실제 객체를 생성하고 조립하는 클래스
각 단계별로 필요한 처리를 수행
최종적으로 생성할 객체(Product)에 대한 참조를 유지
3.
Director : Builder 인터페이스를 사용하여 최종 객체를 생성하는 클래스
Director는 Concrete Builder를 이용하여 객체를 만들며, 사용자는 Director를 통해서 필요한 객체를 받게 된다.
Builder 객체에게 각 단계별로 메소드를 호출하여 객체를 생성
Director는 생성 과정에 대한 구체적인 내용을 알지 않아도 객체를 생성할 수 있다.
4.
Product : Builder를 통해 생성되는 실제 객체를 의미
각 부품은 ConcreteBuilder에 의해 생성되고 조립됩니다.

작동방식

1.
Client는 Director 객체를 생성
2.
Director 객체는 Concrete Builder 객체를 생성
3.
Director 객체는 Builder 객체에게 각 단계별 메소드를 호출하여 객체를 생성
4.
Builder 객체는 각 단계별 처리를 수행하고 Product 객체를 생성
5.
Director 객체는 Builder 객체로부터 Product 객체를 받아 Client에게 전달

장점

유연성 향상 : 객체 생성 과정을 단계별로 수행하여 유연성을 향상
확장성 향상 : 새로운 단계를 추가하거나 기존 단계를 변경하여 쉽게 확장할 수 있다.
코드 재사용성 향상 : Builder 인터페이스를 통해 코드 재사용성을 향상시킬 수 있다.
복잡성 감소 : 객체 생성 과정을 단계별로 분리하여 코드의 복잡성을 감소시킬 수 있다.

단점

오버헤드 발생 : Builder 객체를 추가적으로 생성해야 하기 때문에 오버헤드가 발생할 수 있다.
복잡성 증가 : 단계별 처리가 많아지면 코드 구조가 복잡해질 수 있다.

사용 예시

자동차 제조 : 자동차 제조 과정은 여러 단계로 구성됩니다. Buiilder 패턴을 사용하면 각 단계별로 필요한 부품을 조립하여 최종 자동차를 제조할 수 있다.
문서 작성 : 문서 작성 과정은 여러 단계로 구성됩니다. Builder 패턴을 사용하면 각 단계별로 필요한 내용을 추가하여 최종 문서를 작성할 수 있다.
네트워크 요청 : 네트워크 요청 과정은 여러 단계로 구성됩니다. Builder 패턴을 사용하면 각 단계별로 필요한 정보를 설정하여 최종 네트워크 요청을 생성할 수 있습니다.

사용법

1. Builder 인터페이스 정의

a.
객체 생성 과정을 정의하는 인터페이스를 정의
b.
각 단계별로 필요한 메소드를 선언
public interface Builder { void setPart1(String part1); void setPart2(String part2); void setPart3(String part3); // Builder 인터페이스를 상속하는 객체는 Product 객체를 반환하는 build 메소드 구현 Product build(); }
Java
복사

2. Concrete Builder 구현

a.
Builder 인터페이스를 구현하는 클래스를 구현
b.
각 단계별 처리를 수행
public class ConcreteBuilder implements Builder { private String part1; private String part2; private String part3; @Override public void setPart1(String part1) { // 매개변수로 들어온 String part1을 ConcreteBuilder의 part1의 할당 this.part1 = part1; } @Override public void setPart2(String part2) { this.part1 = part2; } @Override public void setPart3(String part3) { this.part1 = part3; } @Override public Product build() { // set 메소드를 사용하여 할당된 변수들을 가지고 Product 객체를 생성 후 반환 return new Product(part1, part2, part3); } }
Java
복사

3. Director 클래스 구현

a.
Builder 객체를 사용하여 최종 객체(Product)를 생성하는 클래스를 구현
b.
Builder 객체에게 각 단계별로 메소드를 호출하여 객체를 생성
public class Director { public Product construct(Builder builder) { builder.setPart1("part1"); builder.setPart2("part2"); builder.setPart3("part3"); return builder.build(); } }
Java
복사

4. Client에서 사용

a.
Director 객체를 생성하고 Concrete Builder 객체를 전달
b.
Director 객체의 construct() 메소드를 호출하여 최종 객체를 생성
public class Client { public static void main(String[] args) { // Director 객체 생성 Director director = new Director(); // ConcreteBuilder를 사용하여 Builder 객체 생성 Builder builder = new ConcreteBuilder(); // Director 객체의 construct() 메소드를 사용하여 Product 객체 생성 Product product = director.construct(builder); System.out.println(product.getPart1()); System.out.println(product.getPart2()); System.out.println(product.getPart3()); } }
Java
복사

Fluent 인터페이스 패턴과 함께 사용

1.
Builder 인터페이스의 메소드를 메소드 체이닝 방식으로 호출할 수 있도록 Fluent 인터페이스 패턴을 함께 사용할 수 있다.
2.
코드를 더욱 간결하게 만들 수 있다.
public interface Builder { Builder setPart1(String part1); Builder setPart2(String part2); Builder setPart3(String part3); // Builder 인터페이스를 상속하는 객체는 Product 객체를 반환하는 build 메소드 구현 Product build(); } public class ConcreteBuilder implements Builder { private String part1; private String part2; private String part3; @Override public Builder setPart1(String part1) { // 매개변수로 들어온 String part1을 ConcreteBuilder의 part1의 할당 this.part1 = part1; } @Override public Builder setPart2(String part2) { this.part1 = part2; } @Override public Builder setPart3(String part3) { this.part1 = part3; } @Override public Product build() { // set 메소드를 사용하여 할당된 변수들을 가지고 Product 객체를 생성 후 반환 return new Product(part1, part2, part3); } } public class Client { public static void main(String[] args) { Product product = new ConcreteBuilder() .setPart1("part1") .setPart2("part2") .setPart3("part3") .build(); System.out.println(product.getPart1()); System.out.println(product.getPart2()); System.out.println(product.getPart3()); } }
Java
복사
1.
유연성 향상
a.
Builder 인터페이스를 매개변수로 받으면 다양한 Concrete Builder 클래스를 사용 가능
b.
다양한 구성의 객체를 생성할 수 있도록 유연성을 향상 가능
2.
확장성 향상
a.
Builder 인터페이스를 구현하는 새로운 클래스를 추가하거나 기존 Concrete Builder 클래스를 변경해도 Director 객체를 수정할 필요가 없다.
b.
코드의 확장성을 향상
3.
코드 재사용성 향상
a.
Builder 인터페이스는 추상화된 인터페이스
b.
Director 객체는 Builder 인터페이스를 통해 Concrete Builder 클래스와 상호작용하므로 코드 재사용성을 향상시킬 수 있다.
4.
의존성 감소
a.
Director 객체는 Concrete Builder 클래스에 직접 의존하지 않는다.
b.
이는 코드의 결합도를 감소시키고 유지 관리를 용이하게 한다.
5.
추상화 수준 유지
a.
Director 객체는 객체 생성 과정의 구체적인 내용을 알지 않는다.
b.
이는 추상화 수준을 유지하고 코드의 이해도를 향상시킨다.
1.
생각보다 멍청한 질문이었다. Concrete Builder 는 추상화된 인터페이스 Builder를 구현한 클래스니까 사용이 가능하다.
2.
director에 전달된 Builder는 Client에서 Product를 생성할 때 Builder 를 Concrete Builder로 할당하고 할당된 Build를 가지고 construct() 메소드를 실행하니 전달된 Builder는 Concrete Builder이다.
Builder를 구현하는 Concre Builder 가 2개 이상일 경우 어떤 Concrete Builder를 사용하나?
1.
이 질문도 당연히 멍청한 질문이다. Client에서 해당된 Builder 를 선언할때 다른 Concrete Builder를 할당하면 된다.
Client 객체에서 Concrete Builder를 사용해서 Builder 객체를 생성하는 이유
1.
이 질문은 위와 같다. Director의 construct 메소드가 Builder를 매개변수로 받는 이유와 같다.