설모의 기록

[디자인 패턴] Builder 패턴 본문

디자인패턴

[디자인 패턴] Builder 패턴

HA_Kwon 2018. 6. 16. 23:57

빌더 패턴(Builder Pattern)

안드로이드 프로젝트 또는 자바 프로젝트를 진행하다보면 많은 라이브러리를 사용하게 되는데요. 라이브러리에서 제공하는 객체를 사용할 때 빌더 패턴이 자주 보입니다. 

예전에는 그냥 구현한 코드인 줄 알았는데, 여러 곳에서 똑같은 패턴을 사용하는 것을 보고 공부하게 되었습니다. 

서버와 HTTP 통신을 하기 위해 사용하는 라이브러리인 Retrofit2를 사용할 때에도 빌더 패턴을 이용하게 됩니다. 

그렇다면 빌더 패턴은 무엇일까요?


클래스 작성

객체지향 프로그래밍 언어를 사용하면 클래스를 사용해 객체의 틀을 정의하게 되는데요. 흔히 작성하는 클래스 코드 예제를 살펴보겠습니다.

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
public class Family {
    private String father;
    private String mother;
    private String brother;
    private String sister;
    private String address;
    private int memberCount;
 
    // 형제자매가 없는 경우
    public Family(String father, String mother, String address, int memberCount) {
        this.father = father;
        this.mother = mother;
        this.address = address;
        this.memberCount = memberCount;
    }
 
    // (오빠/형/남동생)가 있는 경우
    public Family(String father, String mother, String brother, String address, int memberCount) {
        this.father = father;
        this.mother = mother;
        this.brother = brother;
        this.address = address;
        this.memberCount = memberCount;
    }
 
    ...
}


위의 예제는 가족 클래스입니다. 

가족의 구성원은 다양합니다. 그 요구조건을 모두 만족하려면 생성자를 여러개 만들어야 하고, 생성자 밑에는 getter, setter 메소드도 작성해야 합니다. 

아래의 예제를 보면 불편한 점을 더 발견하실 수 있습니다.




1
2
3
4
public static void main (String[] args) {
    Family family1 = new Family("아빠""엄마""강서구"3);
    Family family2 = new Family("아빠""엄마""오빠","강서구"4);
}


위의 코드는 객체를 생성하는 코드입니다. 

만약 가족 구성원에 할머니, 할아버지, ... 으로 구성되어 있다면 할머니 이름을 넣는 파라미터가 몇번째인지 헷갈릴수도 있으며 많은 파라미터를 모두 기억하고 넣어야하기 때문에 불편합니다. 

이럴 때 빌더 패턴을 사용합니다.





빌더 패턴 예제 코드

public class Student {
private final String name;
private final int age;
private final String university;
private final String address;
private final int sex;

private Student(Builder builder) {
this.name = builder.name;
this.age = builder.age;
this.university = builder.university;
this.address = builder.address;
this.sex = builder.sex;
}

public static class Builder {
// 필수로 받아야하는 인자 (Required parameter)
private final String name;

// 선택적으로 받을 인자 (Optional parameter) -> 초기화를 해야 합니다.
private final int age = 0;
private final String university = "";
private final String address = "";
private final int sex = 0;

// Builder 객체로 this를 반환하면 체이닝 패턴을 사용할 수 있습니다.
public Builder(String name) {
this.name = name;
}

public Builder age (int age) {
age = age;
return this;
}

public Builder university (String university) {
university = university;
return this;
}

public Builder address (String address) {
address = address;
return this;
}

public Builder sex (int sex) {
sex = sex;
return this;
}

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

작성할 클래스 내부에 Nested class인 Builder 클래스를 추가합니다. 

그리고 그 안에 Required parameter와 Optional parameter를 구분해 선언한 후 그에 따라 값을 지정하는 메소드를 작성합니다. 

마지막으로 build() 메소드를 통해 Student 객체를 생성해 각 멤버변수를 복사한 후 반환하는 것입니다.


이 패턴을 사용한다면 각 인자의 의미를 파악하기가 쉽습니다. 또한 setter 함수가 따로 없기 때문에 불변 객체를 만들 수 있어 객체의 일관성이 깨지지 않습니다. 

그리고 build() 메소드가 잘못된 인자 값이 없는지 검증하도록 코드를 수정할 수도 있기 때문에 에외처리 또한 가능합니다. 

객체를 생성하는 방법은 아래와 같습니다.


public static void main (String[] args) {
// 빌더 패턴 사용 예제1
Student.Builder student = new Student.Builder("권현아");
student.university("성신여자대학교");
student.address("강서구");
student.age(24);
student.sex(1);
Student hyeona = student.build();

// 빌더 패턴 사용 예제2
Student kwonHyeona = new Student.Builder("권현아")
.university("성신여자대학교")
.address("강서구")
.age(24)
.sex(1)
.build();

}

1번처럼 각 메소드를 하나씩 사용해도 되고, 2번처럼 체이닝 패턴을 사용해 한번에 객체를 생성할 수도 있습니다.


이렇게 빌더 패턴에 대해 알아보았습니다.

Comments