builder 패턴 - f-lab-edu/jshop GitHub Wiki

지금까지 무분별하게 Setter 를 사용해 객체를 변경해왔다. 그러다 보니 객체에 대한 보호가 되지 않고 유지보수에 안좋은 영향을 주게됨을 알게되었다.

setter의 사용을 최소화하고 builder 패턴을 사용해 객체에 대한 캡슐화를 제공하고 노출을 최소화하도록 변경했다.

builder 패턴

builder 패턴은 생성패턴으로, 특정 객체를 생성하기 위한 패턴이다. 메서드 체이닝 형태로 필요한 필드를 채우고 최종적으로 build를 하게됨으로써 객체를 생성한다.

빌더 패턴을 사용하면 객체의 노출을 최소화할 수 있고, 유연한 생성이 가능하다.

빌더 패턴의 구현예제는 다음과 같다.

  • 생성하려는 객체의 빌더클래스를 생성한다.
  • 빌더 클래스에는 객체의 필드와 동일한 필드를 갖는다.
  • 빌더 클래스의 필드를 채우는 메서드를 통해 필드를 채운다.
  • 빌드 메서드를 사용해 객체를 생성한다.
public class Response<T> {

  private final T data;
  private final String message;
  private final ErrorCode error;

  public static <T> ResponseBuilder<T> builder() {
    return new ResponseBuilder<T>();
  }

  public static class ResponseBuilder<T> {

    private T data;
    private String message;
    private ErrorCode error;

    public ResponseBuilder<T> data(T data) {
      this.data = data;
      return this;
    }

    public ResponseBuilder<T> message(String message) {
      this.message = message;
      return this;
    }

    public ResponseBuilder<T> error(ErrorCode errorCode) {
      this.error = errorCode;
      return this;
    }

    public Response build() {
      return new Response(this.data, this.message, this.error);
    }
  }
}

빌더 패턴의 경우 대부분의 클래스에서 상당히 유사한 형태를 가진다는 특성이 있다. lombok 의 @Builder 어노테이션은 빌더클래스를 자동으로 생성해주는 어노테이션이다.

@Builder

https://projectlombok.org/features/Builder

이 어노테이션은 객체를 생성하는데 필요한 중복코드를 자동으로 생성할 수 있는 어노테이션이다.

어노테이션은 클래스나 생성자, 메서드에서 사용할 수 있다. 일반적으로 클래스나 생성자에서 사용한다

Foo클래스에서 Builder를 사용하게 되면 다음과 같은 일이 진행된다.

  • 빌더를 생성하는 FooBuilder 라는 정적 클래스가 생성된다.
  • 빌더클래스를 생성하는 builder() 정적 메서드가 생성된다.
  • 빌더에서 생성할 객체 필드와 동일한 필드가 생성된다.
  • 빌더에서 빈 생성자가 생성된다.
  • 빌더에서 각 필드를 채울수 있는 필드와 동일한 이름의 메서드가 생성된다.
  • 빌더에서 Foo 객체를 생성할 수 있는 build() 메서드를 생성한다.

클래스 @Builder

클래스에서 @Builder 를 사용하면 필드를 모두 포함하는 빌더를 생성하게 된다. 이때 모든 필드에 대한 생성자가 생성된다.

`@AllArgsConstructor(access=AccessLevel.PACKAGE) 와 동일하게 동작한다.

위의 예제에서 @Builder 를 사용한다면 다음과 같이 코드가 줄어들게 된다.

@Builder
public class Response<T> {

  private final T data;
  private final String message;
  private ErrorCode error;
}

하지만 모든 필드에 대한 빌더 setter메서드를 제공하기 때문에 불필요한 빌더 메서드가 생길 수 있다.

생성자 @Builder

생성자에서 @Builder 를 사용하면 생성자에서 받는 필드에 대해서만 Builder 클래스가 생성된다. 즉 필요한 필드만 사용한 Builder를 만들 수 있는 기능이다.

@Singular

컬렉션 필드를 생성하기 위한 어노테이션이다. 컬렉션(List, Set)에 여러개의 값을 넣을때 사용한다.

정리

객체를 생성하기 위한 다양한 방법들이 있다. 객체를 다양한 방법으로 생성하기 위해 생성자를 여러개 만들경우, 코드가 길어지고 유지보수성이 떨어지는 측면도 있다.

그렇다고 setter를 사용해 객체를 만들자니 무분별한 내부 데이터 노출로 일관성이 떨어질수도 있다.

builder 패턴은 객체를 안전하게 생성하는 방법중 하나다. 노출을 최소화 하고, 원하는 필드만을 사용해 객체를 초기화할 수 있다는 측면에서 유연하게 사용할 수 있다는 장점도 있다.