본문 바로가기

개발 지식/Java

빌더 패턴(Builder Pattern) 이란

반응형

빌더 패턴(Builder Pattern)은 객체를 생성할 때 사용하는 패턴을 의미한다.

//빌더 패턴 예시
Mebmer customer = Member.build()
                        .name("홍길동")
                        .age(30)
                        .build();

위는 자바 코드다. 위와 같은 예시 코드가 빌더 패턴이라고 볼 수 있겠다.

 

Builder 패턴을 사용하는 이유

  1. 객체 생성을 깔끔하게 하기 위함
    • 점층적 생성자 패턴(telescoping constructor pattern)
    • 자바빈 패턴 = 점층적 생성자 패턴의 대안
    • 빌더 패턴 = 자바빈 패턴의 대안
  2. 객체 생성을 유연하게 하기 위함
    • 빌더 인터페이스를 작성하여 다양한 빌더를 사용하는 방법

 

객체 생성을 '깔끔하게' 하기 위한 빌더 패턴 사용

점층적 생성자 패턴

점층적 생성자 패턴을 만드는 방법은 다음과 같다.

  1. 필수 인자를 받는 필수 생성자를 하나 만든다.
  2. 1개의 선택적 인자를 받는 생성자를 추가한다.
  3. 2개의 선택적 인자를 받는 생성자를 추가한다.
  4. 최종적으로 모든 선택적 인자를 다 받는 생성자를 추가한다.
// 점층적 생성자 패턴 코드 예시 : 회원가입 관련 코드
public class Member {
    private final String name;       //필수 인자
    private final String location; //선택적 인자
    private final String hobby;    //선택적 인자

    /** 필수 생성자 **/
    public Member(String name) {
        this(name, "출신지역 비공개", "취미 비공개");
    }

    /** 1개의 선택적 인자를 받는 생성자 **/
    public Member(String name, String location) {
        this(name, location, "취미 비공개");
    }

    /** 모든 선택적 인자를 다 받는 생성자 **/
    public Member(String name, String location, String hobby) {
        this.name = name;
        this.location = location;
        this.hobby = hobby;
    }
}
  • 장점 : new Member("홍길동", "출신지역 비공개", "취미 비공개") 같은 호출이 빈번하게 일어난다면, new Member("홍길동")로 대체할 수 있다.

  • 단점

    • 생성자가 많으므로, 인자가 추가되는 일이 발생하면 코드 수정 다소 복잡.

    • 가독성 저하 => 특히 인자수가 많을 때 호출 코드만 봐서는 의미를 알기 어렵다.

      //가독성 저하의 예시
      NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 3, 35, 27);
      NutritionFacts pepsiCola = new NutritionFacts(220, 10, 110, 4, 30);
      NutritionFacts mountainDew = new NutritionFacts(230, 10);

 

자바빈 패턴(JavaBeans Pattern)

//자바빈 패턴 예시 : setter 메소드 사용
NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);
  • 장점
    • 인자 의미 파악 용이
    • 점층적 생성자 패턴처럼 여러 생성자 만들 필요 X
  • 단점
    • 객체 일관성(consistency)이 깨진다.
      • 1회의 호출로 객체 생성이 끝나지 않음 (생성한 객체에 값을 계속 넣어주고 있음)
    • setter 메소드가 있으므로 변경 불가능(immutable) 클래스를 만들 수가 없다.
      • Thread 안전성을 확보하려면 점층적 생성자 패턴보다 많은 일을 해야 한다.

 

빌더 패턴

//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 {
        /** 필수 인자 **/
        private final int servingSize;
        private final int servings;

        /** 선택적 인자는 기본값으로 초기화 **/
        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) {
            carvohyddrate = 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() 함수가 잘못된 값이 입력되었는지 검증하게 할 수도 있다.

해당 빌더 패턴을 사용하고 싶다면 lombok 이라는 라이브러리의 @Builder 어노테이션으로 쉽게 사용할 수 있다. 다음은 그 예시

//lombok 사용 예시
@Builder
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 class Example {
    /** 일반 사용 예시 **/
    NutritionFacts.NutritionFactsBuilder builder = NutritionFacts.builder();
    builder.calories(230);
    builder.fat(10);
    NutritionFacts facts = builder.build();

    /** 체인 사용 예시 **/
    NutritionFacts facts = NutritionFacts.builder()
        .calories(230)
        .fat(10)
        .build();

}

 

객체 생성을 '유연하게' 하기 위한 빌더 패턴 사용

빌더 패턴을 사용하면 하나의 빌더 객체로 여러 객체를 만드는 것도 가능하다.

// 하나의 빌더 객체로 여러 객체 만드는 예시
public interface Builder<T> {
    public T build();
}

위와 같은 인터페이스를 만들고, 빌더 클래스가 implements 하게 하면 된다.

 


Reference : https://johngrib.github.io/wiki/builder-pattern/

반응형

'개발 지식 > Java' 카테고리의 다른 글

[Java] Jetty 란?  (0) 2020.04.06
Thread Pool이란?  (0) 2020.04.04
[JAVA] Logback 사용법  (0) 2020.02.19
[Java] Generic 사용법  (0) 2020.02.10
[JAVA] JDBC란  (1) 2020.02.05