[객체 생성 패턴] Chapter 4-1. Builder Pattern : 패턴 소개

 

✍️ 빌더 패턴, 패턴 소개

빌더 패턴은 인스턴스를 생성하는 방법과 관련된 패턴으로 생성자와 깊은 연관이 있다.

 

상상해 보라, 클래스의 멤버 변수가 많고 모든 멤버를 한 생성자에서 초기화한다면 생성자를 호출하는 클라이언트는 각각의 매개변수가 어떤 멤버와 매칭하는지 직관적으로 이해하기 힘들고 실수로 매개변수 전달 순서를 바꿔 넣을 수 있다.  

 

하물며 인스턴스는 그 목적에 따라 초기화가 필수인 멤버 변수가 있고 선택적인 멤버 변수가 있다. 초기화가 필수인 멤버 변수만을 위한 새로운 생성자를 정의하거나 선택적인 멤버 변수에 대응하는 매개변수에 null을 전달할 수도 있지만... 코드의 유지 보수나 가독성을 고려했을 때 그리 좋은 방법은 아닌듯하다.

 

이러한 문제들은 빌더 패턴을 사용하면 어느 정도 해결이 되는데, 내 나름대로 빌더 패턴을 정의해 보면

빌더 패턴은 다양한 값으로 구성될 인스턴스를 일관된 프로세스를 통해서 만들 수 있도록 유도하는 패턴이다.

 

🍊 빌더 패턴, 적용 전 코드

여기 TourPlan 클래스가 있다. TourPlan은 여행 일정을 담는 클래스로

여행 이름, 시작일, 종료일 머무는 곳 등 여행에 필요한 다양한 속성을 포함한다. 

@Data
@AllArgsConstructor
@NoArgsConstructor
public class TourPlan {

    private String title;

    private int nights;

    private int days;

    private LocalDate startDate;

    private String whereToStay;

    private List<DetailPlan> plans = new ArrayList<>();

    public void addPlan(int day, String plan) {
        this.plans.add(new DetailPlan(day, plan));
    }

    @Override
    public String toString() {
        return "TourPlan{" +
                "title='" + title + '\'' +
                ", nights=" + nights +
                ", days=" + days +
                ", startDate=" + startDate +
                ", whereToStay='" + whereToStay + '\'' +
                ", plans=" + plans +
                '}';
    }
}

 

App은 여행 상품인 TourPlan을 만드는 클라이언트 코드에 해당한다. 본 예제에선 2박 3일 제주 여행 일정을 담은 인스턴스를 생성하고 있다.

public class App {
    public static void main(String[] args) {
        TourPlan tourPlan = new TourPlan();
        tourPlan.setTitle("제주 여행");
        tourPlan.setNights(2); // 2박
        tourPlan.setDays(3); // 3일
        tourPlan.setStartDate(LocalDate.of(2020, 12, 9));
        tourPlan.setWhereToStay("리조트");
        tourPlan.addPlan(0, "체크인 이후 짐풀기");
        tourPlan.addPlan(0, "저녁 식사");
        tourPlan.addPlan(1, "조식 부페에서 식사");
        tourPlan.addPlan(1, "해변가 산책");
        tourPlan.addPlan(1, "점심은 수영장 근처 음식점에서 먹기");
        tourPlan.addPlan(1, "리조트 수영장에서 놀기");
        tourPlan.addPlan(1, "저녁은 BBQ 식당에서 스테이크");
        tourPlan.addPlan(2, "조식 부페에서 식사");
        tourPlan.addPlan(2, "체크아웃");
    }
}

 

이번엔 당일치기 부산 여행을 만들어보자. 

이전과 다르게 몇박 며칠인지, 어디서 머무를지에 관한 속성은 설정할 필요가 없어졌다. 

public class App {
    public static void main(String[] args) {
        TourPlan dayTrip = new TourPlan();
        dayTrip.setTitle("[당일치기] 부산 여행");
        tourPlan.setStartDate(LocalDate.of(2022, 5, 15));
        tourPlan.addPlan(0, "광안대교");
        tourPlan.addPlan(0, "감천 문화마을");
        tourPlan.addPlan(0, "부산 아쿠아리움");
        tourPlan.addPlan(0, "귀가");
    }
}

 

여기까지 살펴봤을 때 몇 가지 문제점을 알 수 있는데,

첫째로 장황하게 Setter로 속성값을 설정하는 방식은 일관된 프로세스를 제공하지 못한다. 가령 days를 설정한다면 반드시 nights도 설정해야 하는데 지금 방식에선 이를 강제할 방법이 없다. 즉 객체가 불완전한 상태로 만들어질 수 있다.

 

일관된 프로세스를 제공하기 위한 가장 쉬운 방법으로 생성자를 떠올릴 수 있는데 이는 하나의 방법이 될 수도 혹은 또 다른 문제가 될 수 있다. 두 번째 문제점을 살펴보자.

 

둘째로, 인스턴스가 가지는 목적에 따라 생성자의 수가 선형적으로 늘어날 수 있다는 것이다. 가령 2박 3일 여행당일치기 여행은 필요로 하는 속성이 다르므로 새로운 생성자가 정의돼야 한다.

// 당일치기 여행의 생성자
public TourPlan(String title, LocalDate startDate, List<DetailPlan> plans){
    this.title = title;
    this.startDate = startDate;
    this.plans = plans;
}

 

만약 자유여행을 표현하는 인스턴스를 만들어야 한다면 아래와 같은 생성자가 또 추가될 것이다.

public TourPlan(String title, LocalDate startDate){
    this.title = title;
    this.startDate = startDate;
    this.plans = plans;
}

 

이처럼 인스턴스가 가지는 목적에 따라 필요로 하는 속성이 다르고 이를 대응하기 위해 생성자를 하나씩 추가하다 보면 그 수를 헤아릴 수 없게 될 수 있다. 무엇보다 TourPlan을 사용한 클라이언트 쪽에서 어떤 생성자를 사용해야 할지 헷갈리게 된다.

 

👋 빌더 패턴을 사용하면?

빌더 인터페이스는 인스턴스를 만들기 위한 과정을 추상화한다.

일관된 프로세스를 거쳐 인스턴스를 구성하는 작업이 끝났다면 getProduct 메서드를 호출해 인스턴스를 반환한다.

 

구체적인 인스턴스를 생성하는 건 빌더 구현체이기에 다른 프로세스로 인스턴스를 생성하길 원한다면 새로운 ConcreteBuilder를 정의하면 된다.

 

여기까지만 해도 빌더 패턴이지만 조금 더 고도화 시켜서 빌더와 클라이언트 사이에 디렉터를 둘 수 있는데, 클라이언트가 직접 빌더의 모든 API를 사용하는 게 아닌 디렉터를 통해서 간단하게 인스턴스를 얻어올 수 있고 코드를 재사용할 수 있다는 장점을 가지고 있다.

 

 

인프런의 백기선님의 강의 코딩으로 학습하는 GoF의 디자인 패턴을 참고해서 작성했습니다.

 

코딩으로 학습하는 GoF의 디자인 패턴 - 인프런 | 강의

디자인 패턴을 알고 있다면 스프링 뿐 아니라 여러 다양한 기술 및 프로그래밍 언어도 보다 쉽게 학습할 수 있습니다. 또한, 보다 유연하고 재사용성이 뛰어난 객체 지향 소프트웨어를 개발할

www.inflearn.com