본문 바로가기

코딩/Software Architecture

[GoF Design Pattern] Builder

*Builder Pattern

Builder : 복잡한 구성의 객체를 효과적으로 생성하는 패턴

2가지의 서로 다른 내용이 있음

1. 생성 시 지정해야 할 인자가 많을 때 사용

2. 객체 생성 시 여러 단계를 순차적으로 거칠 때 이 단계의 순서를 결정해두고 각 단계를 다양하게 구현할 수 있도록 하는 패턴

 

< 1. 생성 시 지정해야 할 인자가 많은 경우>

Car 클래스는 생성 시 에어백 유무, 엔진, 색상, 카메라 센서 유무, AEB 유무 5개의 인자가 필요함.

Car 클래스 생성 시 new를 이용해 위 5개 인자를 넣기 대신, CarBuilder 클래스를 이용하는 방법을 사용할 수 있음.

5개의 인자를 가지는 Car 클래스

public class Car {
    private String engine;
    private boolean airbag;
    private String color;
    private boolean cameraSensor;
    private boolean AEB;
    
    public Car(String engine, boolean airbag, String color,
    boolean cameraSensor, boolean AEB) {
    	this.engine = engine;
        this.airbag = airbag;
        this.color = color;
        this.cameraSensor = cameraSensor;
        this.AEB = AEB;
    }
    
    @Override
    public String toString() {
    	SreingBuilder sb = new StringBuilder();
        sb.append("CAR SPEC").append(engine).... // 차량 정보 출력하도록!
        return sb.toString();
    }
}

Car 객체를 생성하는 CarBuilder 클래스.

각 인자들을 set 하는 메서드와 Car 객체를 생성하는 build 메서드를 가지고 있다.

public class CarBuilder {
    private String engine;
    private boolean airbag;
    private String color;
    private boolean cameraSensor;
    private boolean AEB;
    
    public CarBuilder setEngine(String engine) {
    	this.engine = engine;
        return this;
    }
    
    public CarBuilder setEngine(boolean airbag) {
    	this.airbag = airbag;
        return this;
    }
    
    public CarBuilder setEngine(String color) {
    	this.color = color;
        return this;
    }
    
    public CarBuilder setEngine(boolean cameraSensor) {
    	this.cameraSensor = cameraSensor;
        return this;
    }
    
    public CarBuilder setEngine(boolean AEB) {
    	this.AEB = AEB;
        return this;
    }
    
    public Car build() {
    	return new Car(engine, airbag, color, cameraSensor, AEB);
    }

}

메서드를 연속적으로 호출하는 방식 : Method Cahining

이 방식을 사용하기 위해 CarBuilder 클래스의 set 메서드에서 this를 리턴함.

이를 통해 실제 객체 생성을 지연시킬 수 있음.

public class MainEntry {
    public static void main(String[] args) {
    	Car car1 = new Car("V7", true, "Black", true, false);
        
        Car car2 = new CarBuilder()
            .setABE(false)
            .setAirbag(false)
            .setCameraSensor(true)
            .setColor("White")
            .setEngine("V9")
            .build();
            
        CarBuilder build = new CarBuilder() // 최종 Car 객체 생성을 지연
            .setABE(false)
            .setCameraSensor(true)
            .setColor("White")
            .setEngine("V9");
            
        Random random = new Random();
        
        Car car3 = builder
            .setAirbag(random.nextInt(2) == 0) // 50% 확률로 aribag 유무 결정
            .build(); 		// 최종 Car 생성

< 2. 객체 생성 시 여러 단계를 순차적으로 거칠 때 이 단 게의 순서를 결정해두고 각 단계를 다양하게 구현할 수 있도록 하는 패턴>

 

Builder : 추상 클래스, 무언가를 만드는 책임을 가짐. 만드는 메서드는 추상 클래스로 아래 Builder를 통해 구현

Director: Builder에서 제공하는 메서드들을 정해진 순서대로 호출

무언가를 만드는 Builder의 메서드를 구체적으로 구현

 - PlainTextBuilder : Data 객체를 평범한 문자열로 만듦

 - JSONBuilder : Data 객체를 JSON 형태의 문자열로 만듦

 - XMLBuilder : Data 객체를 XML 형태의 문자열로 만듦

Data 클래스의 객체는 이름, 나이의 정보를 가지고 있음

public class Data {
    private String name;
    private int age;
    
    public Data(String name, int age) {
    	this.name = name;
        this.age = age;
    }
    
    public String getName() {
    	return name;
    }
    
    public int getAge() {
    	return age;
    }
}

Data를 가공해 필요한 데이터로 만드는 Builder 추상 클래스.

public abstract class Builder {
    protected Data data;
    
    public Builder(Data data) {
    	this.data = data;
    }
    
    public abstract String head();
    public abstract String body();
    public abstract String foot();
    // 위 매서드를 어느 순서로, 어떤 방식으로 진행할지는 Director 클래스가 정함
}

Builder클래스 메서드의 작동 순서,  방식을 정하는 Director 클래스

public class Director {
    private Builder builder;
    
    public Director(Builder builder) {
    	this.builder = builder
    }
    
    public String build() {
    	StringBuilder sb = new StringBuilder();
        
        sb.append(builder.head());
        sb.append(builder.body());
        sb.append(builder.foot());
        
        return sb.toString();
}

Builder 클래스를 상속하는 여러 클래스들

 - PlainTextBuilder

public class PlainTextBuilder extends Buillder {

    public PlainTextBuilder(Data data) {
    	super(data);
    }
    
    @Override
    public String head() {
    	return "";
    }
    
    @Override
    public String body() {
    	StringBuilder sb = new StringBuilder();
        
        sb.append("Name: ")
        .append(data.getName())
        .append(", Age: ")
        .append(data.getAge());
            
    	return sb.toString();
    }
    
    @Override
    public String foot() {
    	return "";
    }
}

 - JSONBuilder

public class JSONBuilder extends Builder {

    public JSONBuilder(Data data) {
    	super(data);
    }
    
    @Override
    public String head() {
    	return "{";
    }
    
    @Override
    public String body() {
    	StringBuilder sb = new StringBuilder();
        
        sb.append("\"Name\": ")
        .append("\"" + data.getName() + "\"")
        .append(", \"Age\": ")
        .append(data.getAge());
            
    	return sb.toString();
    	return 
    }
    
    @Override
    public String foot() {
    	return "}";
    }
}

 - XMLBuilder

public class XMLBuilder extends Builder {

    public XMLBuilder(Data data) {
    	super(data);
    }
    
    @Override
    public String head() {
    	StringBuilder sb = new StringBuilder();
        
        sb.append("<?xml version=\"1.0\" endcoding=\"utf-8\"?>")
        .append("<DATA>");
    
    	return sb.toString();
    }
    
    @Override
    public String body() {
    	StringBuilder sb = new StringBuilder();
        
        sb.append("<Name>")
        .append(data.getName())
        .append("</Name>")
        .append("<Age>")
        .append(data.getAge())
        .append("</Age>");
            
    	return sb.toString();
    }
    
    @Override
    public String foot() {
    	return "</DATA>";
    }
}

구현한 클래스들을 활용해 출력하기

public class MainEntry {
    Data data = new Data("Jane", 25);
    
    Builder builder = new PlainTextBuilder(data);
    Director director = new Director(builder);
    String result = director.build();
    System.out.println(result);
    
    builder = new JSONTextBuilder(data);
    director = new Director(builder);
    result = director.build();
    System.out.println(result);
    
    builder = new XMLTextBuilder(data);
    director = new Director(builder);
    result = director.build();
    System.out.println(result);
}

이 패턴은 Template, Facade, Strategy 패턴을 객체의 생성에 적용한 것으로도 볼 수 있음.

 - Builder를 통해 무언가를 생성할 때 호출되는 메서드를 추상 메서드로 선언하고, 그 메서드를 각 파생 클래스에서 구현한 후 메서드의 호출 방식은 Director 클래스에서 정함 = Template Pattern

 - Director 가 Facade의 역할로써 Builder매서드들의 사용을 단순화 : Facade Pattern

 - 원하는 상황에 맞는 형식으로 변환하는 것 : Strategy Pattern

패턴은 그대로 사용하는 게 아니라 이해 후 다양하게 응용해서 사용하는 도구이므로 다양하게 패턴을 살펴보며 학습하자.

'코딩 > Software Architecture' 카테고리의 다른 글

[GoF Design Pattern] Decorator  (0) 2022.12.05
[GoF Design Pattern] Memento  (0) 2022.12.05
[GoF Design Pattern] Facade  (0) 2022.12.04
[GoF Design Pattern] Flyweight  (0) 2022.12.04
[GoF Design Pattern] Command  (0) 2022.12.04