반응형

빌더 패턴(Builder Pattern)

 

public class Computer {
    String cpu;
    String hdd;
    Integer hddVolume;
    String ssd;
    Integer ssdVolume;
    Boolean bluetoothDongle;

    public Computer(String cpu, String hdd, Integer hddVolume, String ssd, Integer ssdVolume, Boolean bluetoothDongle){
        this.cpu = cpu;
        this.hdd = hdd;
        this.hddVolume = hddVolume;
        this.ssd = ssd;
        this.ssdVolume = ssdVolume;
        this.bluetoothDongle = bluetoothDongle;
    }
}

이렇게 컴퓨터 클래스가 있고 cpu, hdd, ssd, dongle 등등 여러 설정이 필요한 상황이 있다.

이때 컴퓨터를 하나 생성하기 위해서는 수많은 파라미털르 생성자에 줘야한다.

 

어떤 컴퓨터 A는 SSD가 없어 아래와 같은 생성을 해야하고

new Computer("i9-9700k","WD HDD", 1024,null,null,false);

컴퓨터 B는 하드와 블루투스 동글이 없어 아래와 같이 생성했다.

new Computer("i9-9700k",null, null,"T5",2048,null);

하지만 이렇게 나타낸다면 개발자는 어떤 파라미터가 어떤걸 의미하는지 알기 쉽지만, 다른 사람들이 볼때는 무엇을 의미하는지 알기가 힘들다.

 

따라서 이러한 문제를 해결하기 위해 여러 패턴이 존재한다.

 

 

점층적 생성자 패턴(Telescoping Constructor Pattern)

 

점층적 생성자 패턴은 생성자를 overloading하여 여러개의 생성자를 만드는 것을 의미한다.

 

생각해보자. 지금 위의 코드에서 6개의 파라미터가 있는데 최소 1개부터 최대 6개까지의 모든 생성자가 다 있다면

 

6C1 + 6C2 + 6C3 + 6C4 + 6C5 + 6C6 = 63개의 생성자를 만들어야한다.

 

이는 현재 상태에서 올바르지 못한 패턴임을 알 수 있다.

 

 

자바 빈 패턴(Java Bean Pattern)

 

자바 빈 패턴은 setter를 이용하여 객체를 구성해나가는 방법을 의미한다.

Computer A = new Computer();
A.setCpu("i9");
A.setHdd("WD HDD");
A.setHddVolume(1024);
A.setSsd("T5");
A.setSsdVolume(2048);
A.setBluetoothDongle(false);

물론 이렇게 setter를 통해 set하지 않은 값은 null로 관리가 가능해지나 함수 호출 1번만에 객체 생성을 마칠수 없는 단점이 존재한다.

 

 

이러한 각 패턴들이 가지는 문제를 해결하여 장점을 살려낸 것이 빌더 패턴이다.

 

빌더 패턴은 이러한 문제를 해결하기 위해 나타난 디자인 패턴이다.

 

앞서 말했듯이, 객체 생성을 위해 너무많은 파라미터를 전달해야 하는 경우 개발자를 제외한 다른 상대방은 이 파라미터가 어떤 값을 나타내는지 확인하기 힘들다.

 

또 어떠한 객체의 경우에는 특정 인자만으로 생성해야 하는 경우가 발생한다. 이럴 경우, 특정 인자에 해당하는 값을 null로 전달해줘야 하는데, 이는 코드의 가독성 측면에서 매우 좋지 않다는 것을 직감적으로 알 수 있다.

 

빌더 패턴 클래스 다이어 그램

 

 

 

Builder - 객체를 생성하는 추상 인터페이스

 

Concrete Builder - Builder의 구현 클래스. 다른 객체를 생성할 수 있도록 하는 구체적인 클래스.

객체를 만들기 위해 부분(부품)을 생성하고 조립한다.

 

Director - Director클래스는 객체 생성의 정확한 순서(sequenct)를 다루는 부분에 책임이 있다.

이 클래스는 ConcreteBuilder를 인자로 받아서 필요한 동작을 수행한다.

 

Product - Builder를 이용해서 Director가 만들어낸 최종 객체

 

 

 

 

빌더 패턴 예제

 

public class Computer {
    private String cpu;
    private String hdd;
    private Integer hddVolume;
    private String ssd;
    private Integer ssdVolume;
    private String monitor;
    private Boolean bluetoothDongle;

    private Computer(builder builder){
        this.cpu = builder.cpu;
        this.hdd = builder.hdd;
        this.hddVolume = builder.hddVolume;
        this.ssd = builder.ssd;
        this.ssdVolume = builder.ssdVolume;
        this.monitor = builder.monitor;
        this.bluetoothDongle = builder.bluetoothDongle
    }

    public static class builder {
        // Essential value
        private final String cpu;
        private final String hdd;
        private final Integer hddVolume;
        // Selective value
        private String ssd;
        private Integer ssdVolume;
        private String monitor;
        private Boolean bluetoothDongle;

        public builder(String cpu, String hdd, Integer hddVolume){
            this.cpu = cpu;
            this.hdd = hdd;
            this.hddVolume = hddVolume;
        }

        public builder ssd(String ssd, Integer ssdVolume){
            this.ssd = ssd;
            this.ssdVolume = ssdVolume;
            return this;
        }

        public builder monitor(String monitor){
            this.monitor = monitor;
            return this;
        }

        public builder bluetoothDongle(Boolean bluetoothDongle){
            this.bluetoothDongle = bluetoothDongle;
            return this;
        }

        public Computer build(){
            return new Computer(this);
        }
    }
}

 

위와같이 Computer 클래스 내부에 builder를 하나 만들어두고 빌더가 생성을 담당하도록 만들어준다.

 

자세히 보면 Computer 클래스는 private로 생성할 수 없게 만들어두었고, builder를 통해서만 만들 수 있다.

 

이때 빌더의 리턴 타입은 builder 자신으로 builder의 메서드를 호출할 때마다 this에 장착이 되면서 this가 리턴이 되므로 하나하나 장착하는 방식으로 진행 가능하다.

 

마지막으로 build()를 통해 Computer 인스턴스 생성을 해준다.

Computer computer = new Computer.builder("i7", "HDD", 1024)
        .ssd("T7", 1024)
        .monitor("27 inch")
        .bluetoothDongle(true)
        .build();

 

 

 

빌더 패턴 정리

 

빌더패턴은 코드의 가독성도 뛰어나고 사용하기도 쉽다. 다만 코드 사용량이 점층적 생성자 패턴 등보다는 많으므로 인자가 충분히 많은 상황에서 이용 해야하는게 좋다. 물론 새로운 인자가 추가될 수도 있다는 것을 고려해서 사용하는 방법도 괜찮다.



유용한 경우

1. 복잡한 객체를 생성할 때 그 객체를 구성하는 부분들을 생성한 후 그것을 조합 하여도 객체 생성이 가능할 경우, 
즉 객체의 부분을 생성하는 것과 그들을 조합해서 전체 객체를 생성하는 것이 독립적으로 이루어 질 경우

2. 서로 다른 표현방식을 가지는 객체를 동일한 방식으로 생성하고 싶을 경우.

장점
1. Builder 클래스는 Director클래스에게 객체를 생성할 수 있도록 인터페이스를 제공한다. 대신 생성되는 개체의 내부구조나 표현방식 조합 방법은 Builder클래스 내부에 숨긴다. 따라서 Builder패턴에서 생성되는 객체의 내부구조가 바뀔 경우 기존 소스코드에 영향에 주지 않으며 새로운 하위 클래스를 생성하면 된다.

2. 객체를 생성하는 부분과 그것을 실제 표현하고 구성하는 부분을 분리 시켜줌으로써 서로간의 독립성을 기여할 수 있다.

3. 객체를 한꺼번에 생성하는 것이 아니라 부분 생성 후 최종 결과를 얻어가는 방식으로 객체 생성 과정을 상세히 지켜볼 수 있다.

단점
1. 객체 생성을 추가하는 것은 쉬우나 객체를 구성하는 각 부분들을 새롭게 추가하는 것은 어렵다.

2.  생성되는 객체의 구성을 명확히 하여 추가하거나 수정해야하는 부분이 없어야 한다.

 

 

https://leetaehoon.tistory.com/57

https://asfirstalways.tistory.com/350

반응형