Builder Pattern

최준호·2022년 11월 19일
0

Design Pattern

목록 보기
5/7
post-thumbnail

📗 Builder Pattern

빌더 패턴은 객체 인스턴스를 생성해주는 대표적인 패턴으로 spring에서 lombok을 사용했다면 디자인 구조를 몰라도 잘 사용해왔을 것이다.

그 패턴을 직접 구현하는 구조를 짜보자!

📄 패턴 적용 전

⌨️ 집 짓기!

public class House {
    private String elevator;
    private String window;
    private String park;
    private int room;
    private Boolean parking;

    public House() {
    }

    public House(String elevator, String window, String park, int room, Boolean parking) {
        this.elevator = elevator;
        this.window = window;
        this.park = park;
        this.room = room;
        this.parking = parking;
    }

    public void setElevator(String elevator) {
        this.elevator = elevator;
    }

    public void setWindow(String window) {
        this.window = window;
    }

    public void setPark(String park) {
        this.park = park;
    }

    public void setRoom(int room) {
        this.room = room;
    }

    public void setParking(Boolean parking) {
        this.parking = parking;
    }

    @Override
    public String toString() {
        return "House{" +
                "elevator='" + elevator + '\'' +
                ", window='" + window + '\'' +
                ", park='" + park + '\'' +
                ", room=" + room +
                ", parking=" + parking +
                '}';
    }
}

집을 짓기 위해 class를 정의했다. 해당 class를 통해 인스턴스를 생성하려면

public class Client {
    public static void main(String[] args) {
        House apartment = new House();
        apartment.setElevator("현대");
        apartment.setWindow("고급 창문");
        apartment.setPark("호수 공원");
        apartment.setParking(true);
        apartment.setRoom(500);

        House building = new House();
        building.setParking(false);
        building.setRoom(10);
        building.setWindow("일반 창문");

        System.out.println(apartment.toString());
        System.out.println(building.toString());
    }
}

다음과 같은 set 을 반복하는 코드를 작성하거나 길어진 생성자에 직접 매개변수를 일일히 넣어주어 생성해야한다.

이제 이를 builder 패턴을 적용시켜보자!

📄 패턴 적용 후

⌨️ Builder Interface 작성

public interface HouseBuilder {
    HouseBuilder elevator(String elevator);
    HouseBuilder window(String window);
    HouseBuilder park(String park);
    HouseBuilder room(int room);
    HouseBuilder parking(Boolean parking);
    House build();
}

기본 뼈대가 될 인터페이스를 작성했다. 여기서 주목할 점은 chaning을 위해 반환하는 객체 타입이 HouseBuilder를 반환하므로써 계속해서 체이닝이 가능하다는 것이다. 이 말이 이해가 안되면 좀있다 코드로 확인해보자.

⌨️ Builder 구현체 작성

public class DefaultHouseBuilder implements HouseBuilder{
    private String elevator;
    private String window;
    private String park;
    private int room;
    private Boolean parking;

    @Override
    public HouseBuilder elevator(String elevator) {
        this.elevator = elevator;
        return this;
    }

    @Override
    public HouseBuilder window(String window) {
        this.window = window;
        return this;
    }

    @Override
    public HouseBuilder park(String park) {
        this.park = park;
        return this;
    }

    @Override
    public HouseBuilder room(int room) {
        this.room = room;
        return this;
    }

    @Override
    public HouseBuilder parking(Boolean parking) {
        this.parking = parking;
        return this;
    }

    @Override
    public House build() {
        return new House(elevator, window, park, room, parking);
    }
}

⌨️ 집 짓기!

public class Client {
    public static void main(String[] args) {
        HouseBuilder apartBuilder = new DefaultHouseBuilder();
        House apartment = apartBuilder.elevator("현대")
                .window("고급 창문")
                .park("숲 공원")
                .room(500)
                .parking(true)
                .build();
                
        HouseBuilder buildingBuilder = new DefaultHouseBuilder();
        House building = buildingBuilder.window("일반 창문")
                .room(10)
                .parking(false)
                .build();

        System.out.println(apartment.toString());
        System.out.println(building.toString());
    }
}

위의 결과와 동일한 결과를 얻을 수 있는데. 위에서 set을 반복하던 코드에 비해 더 깔끔한 결과를 얻을 수 있었다.

여기서 builder pattern의 또 하나의 장점을 만들 수 있는데. 만약 아파트를 같은 구조로 계속 지어나간다면 계속 builder를 사용할 필요가 없이 지어진 그대로 가져오면 된다.

⌨️ apartment builder 정의해두기

public class ApartBuilder {
    private HouseBuilder houseBuilder;

    public ApartBuilder(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }

    public House prugio(){
        return houseBuilder
                .elevator("대우")
                .window("고급 창문")
                .room(500)
                .park("대우 공원")
                .parking(true)
                .build();
    }

    public House xi(){
        return houseBuilder
                .elevator("GS")
                .window("고급 창문")
                .room(500)
                .park("자이 공원")
                .parking(true)
                .build();
    }
}

다음과 같이 아파트 브랜드별로 찍어내고자 한다면

public class Client {
    public static void main(String[] args) {
        HouseBuilder builder = new DefaultHouseBuilder();
        ApartBuilder apartBuilder = new ApartBuilder(builder);
        House prugio = apartBuilder.prugio();
        House xi = apartBuilder.xi();

        System.out.println(prugio.toString());
        System.out.println(xi.toString());
    }
}

다음과 같은 결과도 확인해볼 수 있다.

📄 패턴의 장점과 단점

⌨️ 장점

복잡한 객체 생성 과정을 숨길 수 있고 더 세부적인 다양한 구조를 작성해볼 수 있다. 또한 지금은 build()에서 바로 new 객체()를 통해 반환하고 있지만 더 자세한 구현체를 얻기 위해 null 체크를 한다던지 들어올 수 없는 값을 체크한다던지 로직을 추가하여 더욱 단단한 객체를 반환해줄 수도 있다.

⌨️ 단점

단점으로는 builder라는 객체를 생성 해주어야하는 성능적인 부분이 존재하고, 다른 디자인 패턴들에서도 함께 존재하는 구조가 복잡해진다는 단점이 있다.

profile
해당 주소로 이전하였습니다. 감사합니다. https://ililil9482.tistory.com

0개의 댓글