[디자인패턴] 빌더 패턴

June·2021년 8월 4일
0

빌더 패턴(Builder Pattern)

생성자에 인자가 많을 때는 빌더 패턴을 고려하라
-Effective Java 규칙 2 - 조슈아 블로크-

빌더 패턴이란?

빌더 패턴은 복잡한 객체를 생성하는 방법을 정의하는 클래스와 표현하는 방법을 정의하는 클래스를 별도로 분리하여, 서로 다른 표현이라도 이를 생성할 수 있는 동일한 절차를 제공하는 패턴입니다.

빌더 패턴생성 패턴(Creational Pattern) 중 하나이다. 생성 패턴은 인스턴스를 만드는 절차를 추상화하는 패턴입니다. 생성 패턴에 속하는 패턴들은 객체를 생성, 합성하는 방법이나 객체의 표현 방법을 시스템과 분리해줍니다.

빌더 패턴을 사용해야 하는 이유

public class ProductDto {
	private int displayInfoId;
	private String placeName;
	private String productContent;
	private String productDescription;
	private int productId;
	private String productImageUrl;

	public ProductDto() {
	}	

	public ProductDto(int displayInfoId, String placeName, String productContent, String productDescription,
			int productId, String productImageUrl) {
		this.displayInfoId = displayInfoId;
		this.placeName = placeName;
		this.productContent = productContent;
		this.productDescription = productDescription;
		this.productId = productId;
		this.productImageUrl = productImageUrl;
	}
    ...
}

위는 실제 프로젝트에서 사용된 코드이다.

속성들이 추가될 때마다 넘겨줘야하는 인자 값들이 계속 늘어나게 된다. 또한 필수가 아닌 값들은 null로 주게되면 무엇이 인자로 들어가는지 정확히 이해하기 어렵다. 또 데이터를 입력하는 순서를 항상 기억해야한다는 부담감이 있다.

new ProductDto(1, null, null, null, 2, null);

또한 지금대로라면 들어오는 인자의 개수가 다를 때마다 생성자를 추가해줘야 한다.

즉 장점은 아래와 같다.

  1. 필요한 데이터만 설정할 수 있음
  2. 유연성을 확보할 수 있음
  3. 가독성을 높일 수 있음
  4. 불변성을 확보할 수 있음

출처: https://mangkyu.tistory.com/163 [MangKyu's Diary]

빌더 패턴 구현

// Effective Java의 Builder Pattern
public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // Required parameters(필수 인자)
        private final int servingSize;
        private final int servings;

        // Optional parameters - initialized to default values(선택적 인자는 기본값으로 초기화)
        private int calories      = 0;
        private int fat           = 0;
        private int carbohydrate  = 0;
        private int sodium        = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings    = servings;
        }

        public Builder calories(int val) {
            calories = val;
            return this;    // 이렇게 하면 . 으로 체인을 이어갈 수 있다.
        }
        public Builder fat(int val) {
            fat = val;
            return this;
        }
        public Builder carbohydrate(int val) {
            carbohydrate = val;
            return this;
        }
        public Builder sodium(int val) {
            sodium = val;
            return this;
        }
        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }

    private NutritionFacts(Builder builder) {
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

위와 같이 하면 다음과 같이 객체를 생성할 수 있다

NutritionFacts.Builder builder = new NutritionFacts.Builder(240, 8);
builder.calories(100);
builder.sodium(35);
builder.carbohydrate(27);
NutritionFacts cocaCola = builder.build();

또는 다음과 같이 사용할 수도 있다.

// 각 줄마다 builder를 타이핑하지 않아도 되어 편리하다.
NutritionFacts cocaCola = new NutritionFacts
    .Builder(240, 8)    // 필수값 입력
    .calories(100)
    .sodium(35)
    .carbohydrate(27)
    .build();           // build() 가 객체를 생성해 돌려준다.
  • 각 인자가 어떤 의미인지 알기 쉽다.
  • setter 메소드가 없으므로 변경 불가능 객체를 만들 수 있다.
  • 한 번에 객체를 생성하므로 객체 일관성이 깨지지 않는다.
  • build() 함수가 잘못된 값이 입력되었는지 검증하게 할 수도 있다.

출처: https://johngrib.github.io/wiki/builder-pattern/

Lombok을 활용한 Builder

0개의 댓글