[객체 생성 패턴] Chapter 4-4. Builder Pattern : 장단점

 

✍️ 빌더 패턴, 장점

빌더 패턴의 첫 번째 장점으로, 생성 과정이 복잡한 인스턴스를 일관된 프로세스로 생성할 수 있다. 동시에 클라이언트가 빌더의 메서드를 체이닝 형태로 호출하며 자연스럽게 인스턴스를 구성하고 최종적으로 객체를 생성하도록 유도할 수 있다. 

Student kangworld = new Student
    .StudentBuilder(32140033)
    .setName("kangworld")
    .setPhoneNumber("010-1234-5678")
    .setGrade("sophomore")
    .getStudent();

 

두 번째 장점으로, 멤버 변수의 초기화와 검증을 각각의 멤버 변수별로 분리해서 작성할 수 있다. 가령 멤버 변수의 초기화를 검증하는 로직이 하나의 생성자에 다 포함되었다면 생성자의 크기가 비대해질 것이다. 

 

아래와 같이 생성자에 초기화 검증 코드를 다 때려 박는 것보다

public class Student {
    private int id;

    private String name;
    private String grade;
    private String phoneNumber;

    public Student(int id, String name, String grade, String phoneNumber) {
        if (!grade.equals("freshman") && !grade.equals("sophomore")
                && !grade.equals("junior") && !grade.equals("senior")) {
            throw new IllegalArgumentException(grade);
        }

        if (!phoneNumber.startsWith("010")) {
            throw new IllegalArgumentException(phoneNumber);
        }

        this.id = id;
        this.name = name;
        this.grade = grade;
        this.phoneNumber = phoneNumber;
    }
}

 

빌더의 각각의 메서드에서 검증 과정을 분담하는 것이 유지 보수에 유리할 것이다.

public static class StudentBuilder {
    private int id;

    private String name;
    private String grade;
    private String phoneNumber;

    public StudentBuilder(int id) {
        this.id = id;
    }

    public StudentBuilder setName(String name) {
        this.name = name;

        return this;
    }

    public StudentBuilder setGrade(String grade) {
        if (!grade.equals("freshman") && !grade.equals("sophomore")
                && !grade.equals("junior") && !grade.equals("senior")) {
            throw new IllegalArgumentException(grade);
        }

        this.grade = grade;

        return this;
    }

    public StudentBuilder setPhoneNumber(String phoneNumber) {
        if (!phoneNumber.startsWith("010")) {
            throw new IllegalArgumentException(phoneNumber);
        }
        
        this.phoneNumber = phoneNumber;

        return this;
    }

    public Student getStudent() {
        return new Student(this);
    }
}

 

세 번째 장점으로, 디렉터를 사용하면 인스턴스를 만드는 구체적인 과정을 디렉터로 숨기고 클라이언트 코드는 디렉터를 호출함으로써 코드가 간결해지는 장점이 있다.

 

네 번째 가장 중요한 장점인데, 서로 다른 빌더 구현체를 주입함으로써 동일한 프로세스에서 서로 다른 객체를 생성할 수 있다.

 

가령 TourPlan을 상속받은 VipTourPlan과 VVipTourPlan이 있고, VipTourBuilder의 getPlan은 VipTourPlan을 반환하고 VVipTourBuilder는 VVipTourPlan을 반환한다고 가정하면 동일한 프로세스로 인스턴스를 생성하되 주입된 빌더에 따라 서로 다른 객체를 생성할 수 있게 된다.

public class TourDirector {
    private TourPlanBuilder builder;

    public TourDirector(TourPlanBuilder builder) {
        this.builder = builder;
    }

    public TourPlan jejuTripPlan() {
        return builder.title("제주도 여행")
                .nightsAndDays(2, 3)
                .startDate(LocalDate.of(2020, 12, 9))
                .whereToStay("리조트").addPlan(0, "체크인")
                .addPlan(0, "한라산 등반")
                .addPlan(1, "한라봉 먹기")
                .addPlan(2, "흑돼지 먹기")
                .addPlan(3, "귀가")
                .getPlan();
    }
 }

 

🍊 빌더 패턴, 단점

빌더 패턴의 단점으로, 클라이언트는 구체적인 인스턴스를 생성하기 전에 반드시 빌더를 생성해야 한다.

 

또 다른 단점으로 여느 디자인 패턴이 가지는 단점으로 관리해야 할 클래스가 많아지고 구조가 복잡해지는 단점이 있다. 

 

 

 

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

 

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

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

www.inflearn.com