티스토리 뷰

반응형

1. 빌더 패턴이란?

 빌더 패턴(Builder Pattern)은 추상 팩토리 패턴(Abstract Factory Pattern)과 팩토리 메소드 패턴(Factory Method Pattern)과 함께 객체의 생성과 관련된 디자인 패턴이다.


 빌더 패턴은 생성자에 들어갈 매개 변수에 대해 차례차례 매개 변수를 받아들이고 모든 매개 변수를 받은 뒤에 객체를 생성하는 구조이다. 그래서 매개 변수가 많을 때 쓰면 유리하다. 이렇게 글로 설명하면 명확하지 않으니 아래에 예를 들어 설명할 것이다.





2. 빌더 패턴을 왜 쓰는가?

 빌더 패턴을 설명하기 위해 '학생 정보'를 저장하는 클래스를 소개한다.


public class StudentInfo {
    private String firstName;
    private String lastName;
    private String studentNum;
    private String major;
    private int creditForGraduate;

    public StudentInfo (String firstName, String lastName, String studentNum) {
        this(firstName, lastName, studentNum, "", -1);
    }
    
    public StudentInfo (String firstName, String lastName, 
                        String studentNum, String major, int creditForGraduate) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.studentNum = studentNum;
        this.major = major;
        this.creditForGraduate = creditForGraduate;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public String getStudentNum() {
        return studentNum;
    }

    public String getMajor() {
        return major;
    }

    public void setMajor(String major) {
        this.major = major;
    }

    public int getCreditForGraduate() {
        return creditForGraduate;
    }

    public void setCreditForGraduate(int creditForGraduate) {
        this.creditForGraduate = creditForGraduate;
    }
}

 학생의 이름, 성, 학번, 전공, 졸업이수학점을 저장하는 클래스이다. 이름, 성, 학번은 변하지 않으니(Immutable) Setter 메소드가 빠져있다. 그리고 전공과 졸업이수학점은 처음에 결정되지 않을 수 있으므로 2개의 생성자가 선언되어 있다. Setter 메소드를 반복하지 않기 위한 나름의 묘수라고 할 수 있다.



 예를 들어, "Jake Brigham"이라는 컴퓨터과학과 학생의 정보를 갖는 새로운 객체를 생성할 때는 아래와 같이 한다.


StudentInfo studentInfo
                = new StudentInfo("Jake", "Brigham", "1800221", "CS", 120);

 아직 졸업이수학점과 학과가 결정되지 않은 신입생 "Brandon Knight"라는 학생의 경우는? 생성자를 따로 만들어 놨으므로 쉽게 처리할 수 있다.


StudentInfo studentInfo1 = new StudentInfo("Brandon", "Knight", "1800231");

 만약 전공학과는 결정됐는데 졸업이수학점이 결정되지 않았다면? 객체 생성 시점에 졸업이수학점에 매개변수값을 -1로 전달하는 등의 처리를 해야한다. 적합한 생성자를 새롭게 선언하는 방법도 있지만, 매번 이렇게 할 수 없는 노릇이다. 만약 이 클래스를 구현하는 개발자가 실수로 이름과 성을 바꿔넣는다면? 컴파일 시점에는 이러한 실수를 도저히 알아낼 수 없다. (IntelliJ와 같은 IDE에서는 이러한 실수를 줄여주기도 한다.)


 그러면 자바빈 패턴(Java Bean)으로 구현하는 건 어떨까? 초기화를 돕는 생성자는 모두 빼버리고 오로지 Setter 메소드를 통해서만 내부 정보를 초기화 할수 있도록 하는 것이다. 이렇게하면 개발자의 실수는 줄이겠지만, 소스코드가 너무 복잡해진다.


StudentInfo studentInfo = new StudentInfo();
studentInfo.setFirstName("Jake");
studentInfo.setLastName("Brigham");
studentInfo.setStudentNum("1800221");
studentInfo.setMajor("CS");
studentInfo.setCreditForGraduate(120);

 빌더 패턴을 사용하면 간략하게 객체를 생성하면서도, 개발자의 실수를 줄일 수 있다.



3. 빌더 패턴을 어떻게 쓰는가?

 별도의 Builder 클래스를 선언하여, 기존에 생성자가 처리하던 일들을 위임한다. 클래스의 구성요소를 초기화 하는 메서드의 이름을 알아보기 쉽게 지으면 더 좋다.


public class StudentInfo {
    private String firstName;
    private String lastName;
    private String studentNum;
    private String major;
    private int creditForGraduate;

    // public 생성자는 필요없다. Builder가 만들어 줄꺼니까.
    private StudentInfo (StudentInfoBuilder studentInfoBuilder) {
        this.firstName = studentInfoBuilder.firstName;
        this.lastName = studentInfoBuilder.lastName;
        this.studentNum = studentInfoBuilder.studentNum;
        this.major = studentInfoBuilder.major;
        this.creditForGraduate = studentInfoBuilder.creditForGraduate;
    }

    // Inner Class로 Builder 선언
    public static class StudentInfoBuilder {
        private String firstName;
        private String lastName;
        private String studentNum;
        private String major;
        private int creditForGraduate;

        public StudentInfoBuilder firstName(String firstName) {
            this.firstName = firstName;
            return this;
        }

        public StudentInfoBuilder lastName(String lastName) {
            this.lastName = lastName;
            return this;
        }

        public StudentInfoBuilder studentNum(String studentNum) {
            this.studentNum = studentNum;
            return this;
        }

        public StudentInfoBuilder major(String major) {
            this.major = major;
            return this;
        }

        public StudentInfoBuilder creditForGraduate(int creditForGraduate) {
            this.creditForGraduate = creditForGraduate;
            return this;
        }

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

 빌더 클래스를 원래 클래스의 Inner static Class로 선언했으므로, 아래와 같이 간단하게 생성 가능하다.


StudentInfo studentInfo = new StudentInfo.StudentInfoBuilder()
                .firstName("Jake")
                .lastName("Brigham")
                .studentNum("1800221")
                .major("CS")
                .creditForGraduate(120)
                .build();


4. 빌더 패턴의 장점


1. 불필요하게 생성자를 여러개 만들지 않아도 된다. 


2. 매개변수의 순서에 상관 없이 객체를 만들 수 있다. 


3. 클래스 내부정보를 설정하는 메소드가 명시적이고, 메소드를 유연하게 사용할 수 있다.


 특히, 클래스 내부정보를 초기화하는 메소드를 유연하게 사용할 수 있다는 것은 큰 장점이다. Immutable 객체를 만들 때도 그렇고, 일부 정보를 자동으로 생성할 때도 그렇다. 심지어 메소드 내부에 조건을 추가하여 생성과 동시에 검증을 할 수도 있다.



5. 빌더 패턴의 단점

 

Builder Class를 생성할 때의 오버헤드.


클래스의 내부정보가 적을 때는 쓸 필요가 적음.


 개인적인 생각으로는 성능이 매우 중요한 상황이 아니라면 오버헤드는 무시해도 될 수준일 것 같다. 그리고 클래스의 내부정보는 삭제되는 것 보단 추가되는 경우가 훨씬 많으므로, 미리 빌더패턴으로 구현해 놓는 것이 유리할 수 있다.




-끝-





출처 및 참고

https://jdm.kr/blog/217

https://ko.wikipedia.org/wiki/%EB%B9%8C%EB%8D%94_%ED%8C%A8%ED%84%B4

반응형
최근에 올라온 글
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함
Total
Today
Yesterday