mojo's Blog

Builder Pattern 본문

Design Patterns

Builder Pattern

_mojo_ 2024. 6. 9. 12:48

Builder Pattern

 

목적: 쉽게 상호 교환할 수 있는 알고리즘 기반으로 객체를 동적 생성

사용시기

- 생성 프로세스에 대한 런타임 제어 필요

- 생성 알고리즘의 여러 표현이 필요

- 객체 생성 알고리즘을 시스템에서 분리

- 핵심 코드를 변경하지 않고 새로운 생성 기능을 추가

 

※ Structure

 


- Director: 최종 제품에 필요한 부품이 무엇인지를 알고 있다.

- Concrete builder: 부품을 생산하고 최종 제품에 추가하는 방법을 알고 있다.

 

※ Participants

(1) Client

      제품을 제작할 director, concrete builder를 선택함

      concrete builder에게 최종 시공된 제품을 반환하도록 요청함

(2) Director

      제품을 만드는 데 어떤 단계가 필요한지 알고 있음

      그러나 각 단계가 어떻게 수행되는지는 모름

(3) Builder

      제품 객체의 일부를 만들기 위한 추상 인터페이스 지정

(4) Concrete Builder

      Builder 인터페이스를 구현하여 제품의 부품을 구성하고 조립함

      생성된 표현을 정의하고 추적함

(5) Product

      시공 중인 복합 객체를 나타냄

 

※ Example: Building Airplanes

비행기를 만드는 예시를 살펴보도록 한다.

클라이언트를 제외한 나머지 참여자들의 구성은 아래와 같이 나타낼 수 있다.

- Director: AerospaceEngineer

- Builder: AirplaneBuilder

- Concrete Builder: CropDuster, FighterJet, Glider

- Product: Airplane

 

Director Code

public class AerospaceEngineer {
    private AirplaneBuilder airplaneBuilder;
    
    public void setAirplaneBuilder(AirplaneBuilder ab) {
    	airplaneBuilder = ab;
    }
    public Airplane getAirplane() {
    	return airplaneBuilder.getAirplane();
    }
    public void constructAirplane() {
        airplaneBuilder.createNewAirplane();
        airplaneBuilder.buildWings();
        airplaneBuilder.buildPowerplant();
        airplaneBuilder.buildAvionics();
        airplaneBuilder.buildSeats();
    }
}

setAirplaneBuilder 메서드에서 Builder 클래스의 객체 ab 를 받아와서 할당하게 된다.

이때, Builder 클래스의 객체 ab 의 구체적인 하위 클래스(ex: CropDuster)가 무엇인지는 알 수 없다.

constructAirplane 메서드를 통해 객체 ab를 통해 비행기를 짓는 작업이 수행된다.

이때, 비행기가 각 단계별로 어떻게 수행되어지는지 알 수 없다.

 

Builder Code

public abstract class AirplaneBuilder {
    protected Airplane airplane;
    protected String customer;
    protected String type;
    
    public Airplane getAirplane() {
    	return airplane;
    }
    public void createNewAirplane() {
    	airplane = new Airplane(customer, type);
    }
    public abstract void buildWings();
    public abstract void buildPowerplant();
    public abstract void buildAvionics();
    public abstract void buildSeats();
}

Builder 클래스는 추상적 클래스임을 유의한다. (Concrete Builder 가 이를 extend 함)

getAirplane 메서드를 통해 생성한 비행기를 반환한다.

createNewAirplane 메서드를 통해 비행기 객체를 생성한다.

추상적 메서드들은 Concrete Builder 를 통해 구현이 이뤄지게 된다.

 

Concrete Builder Code

public class CropDuster extends AirplaneBuilder {
    CropDuster(String customer) {
        super.customer = customer;
        super.type = "Crop Duster v3.4";
    }
    public void buildWings() {
    	airplane.setWingspan(9f);
    }
    public void buildPowerplant() {
    	airplane.setPowerplant("single piston");
    }
    public void buildAvionics() {}
    public void buildSeats() {
    	airplane.setNumberSeats(1,1);
    }
}

Concrete Builder 세 개중 한 개의 코드를 예시로 들어본다. (CropDuster)

CropDuster 생성자는 customer 를 받아와서 Builder 클래스의 변수에 값을 할당하게 된다.

=> type 의 경우 Concrete Builder 에 대응하는 type 값이 할당되게 됨 (Crop, FighterJet, Glider 등)

그 외에 Builder 클래스에서 추상적 메서드들은 여기서 구현이 이뤄지게 된다.

 

Product Code

public class Airplane {
    private String type;
    private float wingspan;
    private String powerplant;
    private int crewSeats;
    private int passengerSeats;
    private String avionics;
    private String customer;
    
    Airplane (String customer, String type) {
    	this.customer = customer;
    	this.type = type; 
    }
    public void setWingspan(float w) { this.wingspan = w; }
    public void setPowerplant(String p) { this.powerplant = p; }
    public void setAvionics(String a) { this.avionics = a; }
    public void setNumberSeats(int crewSeats, int passengerSeats) {
    	this.crewSeats = crewSeats;
    	this.passengerSeats = passengerSeats;
    }
    public String getCustomer() { return customer; }
    public String getType() { return type; }
}

Concrete Builder 클래스에서 추상적 메서드를 구현한 부분이 호출되게 된다.

예를 들어 buildWings 메서드 내에 airplane.setWingspan(9f) 를 호출하면,

airplane.wingspan 값이 9f 으로 세팅되게 된다.

 

 Client Code

public class BuilderExample {
    public static void main(String[] args) {
        // instantiate the director (hire the engineer)
        AerospaceEngineer aero = new AerospaceEngineer();
        
        // instantiate each concrete builder (take orders)
        AirplaneBuilder crop = new CropDuster("Farmer Joe");
        AirplaneBuilder fighter = new FighterJet("The Navy");
        AirplaneBuilder glider = new Glider("Tim Rice");
        
        // build a CropDuster
        aero.setAirplaneBuilder(crop);
        aero.constructAirplane();
        Airplane completedCropDuster = aero.getAirplane();
    }
}

1. Director를 생성한다. (aero)

2. Concrete builder 를 생성한다. (crop, fighter, glider)

3. Director 내에 builder 객체를 할당할 concrete builder 를 설정한다.

4. Builder 할당이 완료되면, 비행기를 만든다.

5. Director 를 통해 만들어진 비행기 객체를 반환한다.

 

When a Builder Shouldn't Be Used

빌더 패턴을 사용하지 않는 경우는 인터페이스가 안정적이지 않을 때 이다.

- 모든 인터페이스 변경에는 컨트롤러 변경이 필요하며, 추상 클래스 또는 해당 객체에 영향을 미침

- 새 방법을 사용하려면 기본 클래스와 새 방법을 재정의해야 하는 모든 구체적 클래스를 변경해야 함

- 특정 메서드 인터페이스를 변경하려면 이전 메서드를 지원하는 모든 구체적 클래스를 변경해야 함

 

※ Builder v.s. Abstract Factory

- Builder

   객체를 단계별로 구성하고 결과는 이후 단계에서 요청

- Abstract Factory

   요청한 객체를 즉시 반환

   Abstract builder 가 없으며 응용 프로그램에서 직접 팩토리 메서드를 호출

   

'Design Patterns' 카테고리의 다른 글

Decorator Pattern  (0) 2024.06.09
Singleton Pattern  (1) 2024.06.09
Factory Method Pattern & Abstract Factory Pattern  (0) 2024.06.09
Mediator Pattern  (0) 2024.06.06
State Pattern  (0) 2024.06.06
Comments