빌더 패턴 (Builder Pattern)
빌더 패턴(Builder pattern)이란?
객체를 정의하고 그 객체를 생성할 때 보통 생성자를 통해 생성하는 것을 생각한다.
Bag bag = new Bag("name", 1000, "memo");
하지만 생성자를 통해 객체를 생성하는데 몇 가지 단점이 있어 객체를 생성하는 별도 builder를 두는 방법이 있다. 이를 빌더 패턴이라고 한다.
Bag bag = Bag.builder()
.name("name")
.money(1000)
.memo("memo")
.build();
객체를 생성할 수 있는 빌더를 builder() 함수를 통해 얻고 거기에 셋팅하고자 하는 값을 셋팅하고 마지막에 build()를 통해 빌더를 작동 시켜 객체를 생성한다.
빌더 패턴(Builder pattern)을 사용하는 이유
생성자 파라미터가 많을 경우 가독성이 좋지 않다.
위에 예시에서 Bag 클래스는 생성자 파라미터를 3개만 받는다. 3개까지는 괜찮다. 하지만 생성자 파라미터로 받아야하는 값이 수업이 많아진다면? 각 값들이 어떤 값을 의미하는지 이해하기 힘들 것이다.
Bag bag = new Bag("name", 1000, "memo", "abc", "what", "is", "it", "?");
하지만 이를 빌더 패턴으로 구현하면 각 값들은 빌더의 각 값들의 이름 함수로 셋팅이 되지 각각 무슨 값을 의미하는지 파악하기 쉽다.
따라서 생성자로 설정해야하는 값이 많을 경우 빌더를 쓰는 것이 생성자보다 가독성이 좋다. 이는 같은 타입의 다른 변수의 값을 서로 바꿔 넣는 것을 방지할 수도 있다.
Bag bag = Bag.builder()
.name("name")
.money(1000)
.memo("memo")
.letter("This is the letter")
.box("This is the box")
.build();
어떤 값을 먼저 설정하던 상관 없다
생성자의 경우는 정해진 파라미터 순서대로 꼭 값을 넣어줘야한다. 순서를 무시하고 값을 넣으면 에러가 발생하거나 엉뚱한데 값이 들어갈 수 있다.
public Bag(String name, int money, String memo) {
this.name = name;
this.money = money;
this.memo = memo;
}
하지만 빌더 패턴은 빌더의 필드 이름으로 값을 설정하기 때문에 순서에 종속적이지 않다.
그냥 쓰이는 곳에서 어떤 필드를 먼저 설정해야하는지 굳이 순서를 생각할 필요 없이 편하게 설정하면 된다.
Bag bag = Bag.builder()
.name("name")
.memo("memo") // memo를 money 대신 먼저!
.money(1000)
.build();
필요한 데이터만 설정할 수 있다.
예를 들어 User 객체를 생성해야 하는데 age라는 파라미터가 필요 없는 상황이라고 가정하자.
생성자나 정적 메소드를 이용하는 경우라면 우리는 age에 더미 값을 넣어주거나 age가 없는 생성자를 새로 만들어주어야 한다.
// 1. 더미값을 넣어주는 방법
User user = new User("망나니개발자", 0, 180, 150)
// 2. 생성자 또는 정적 메소드를 추가하는 방법
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String name;
private int age;
private int height;
private int iq;
public User (String name, int height, int iq) {
this.name = name;
this.height = height;
this.iq = iq;
}
public static User of(String name, int height, int iq) {
return new User(name, 0, 180, 150);
}
}
이러한 작업이 한 두번이면 번거로워도 작업을 해줄 수 있다. 하지만 결국 요구사항은 계속 변하게 되어있고, 반복적인 변경을 필요로 하면서 시간 낭비로 이어지게 된다. 하지만 빌더를 이용하면 동적으로 이를 처리할 수 있다.
User user = User.builder()
.name("망나니 개발자")
.height(180)
.iq(150).build();
setter가 없으므로 객체 일관성을 유지하여 불변 객체로 생성할 수 있다.
빌더 패턴(Builder pattern)의 구현 - @Builder
위와 같은 이유로 생성자 대신 빌더를 사용하곤 하는데 그렇다면 빌더는 어떻게 구현해야하나?
우선 코드로 빌더를 만들고 그 안에서 멤버 필드별로 값을 설정하고 빌더를 반환하는 함수를 만들면 된다.
하지만 이 방법은 멤버 필드 별로 함수를 생성해야하는 등 생성자보다 더 번거롭고 해당 클래스에 들어갔을 때 빌더로만 몇십줄을 차지해서 코드 읽기도 불편하다. 그래서 나온 것인 @Builder 어노테이션이다.
사용법은 아주 간단하다. 빌더 패턴을 적용할 객체에 @Builder 어노테이션을 달기만 하면된다.
@Builder
public class Bag {
private String name;
private int money;
private STring memo;
}
어노테이션만 달면 빌더가 생기고 위에 썼던 것과 같이 빌더를 통해 객체를 생성할 수 있다.
Bag bag = Bag.builder()
.name("name")
.money(1000)
.memo("memo")
.build();
출처