생성 - Builder

Builder 패턴

다른 생성쪽 패턴과 마찬가지로 객체를 생성하기 위해 사용되는 패턴. Lucene을 활용하면서 lucene query 객체를 생성하는 패턴이 빌더패턴이라고 할 수 있다.

QueryBuilder queryBuilder = fullTextSession.getSearchFactory() 
    .buildQueryBuilder()
    .forEntity(News.class)
    .get();

org.apache.lucene.search.Query fuzzyQuery = queryBuilder
            .keyword()
            .fuzzy()
            .withEditDistanceUpTo(1)
            .withPrefixLength(0)
            .onField("body")
            .matching(keyword)
            .createQuery();

Builder 패턴을 사용하는 이유

  • 핵심 목적 : 객체의 생성 알고리즘과 조립 방법을 분리하는 것이 목적.
    • Immutable 한 객체를 유연하게 생성하기 위해.
    • 코드 가독성이 높아짐.

생성자, setter VS Builder

다음과 같은 Pizza 클래스가 있다고 하자.

public class Pizza {
    private final String menuName; // 필수
    private final String size;     // 선택
    private final double price;    // 선택
}

생성자 패턴

생성자를 이용해 객체를 만드는 방법은 다음과 같다.

public class Pizza {
    private final String menuName; // 필수
    private final String size;     // 선택
    private final double price;    // 선택
    
    // 이름만 인자로 받는 생성자
    public Pizza(String menuName){
        this(menuName, "M", 20.00);
    }

    // 이름과  인자로 받는 생성자
    public Pizza(String menuName, String size){
        this(menuName, size, 20.00);
    }

    public Pizza(String menuName, double size, double price){
        this.menuName = menuName;
        this.size = size;
        this.price = price;
    }
}

// 기본 피자 생성
new Pizza("포테이토 피자");
// 크게 만들고 싶을 때.
new Pizza("포테이토 피자", "L");
// 크게 만들어서 비싸게 받을 때
new Pizza("포테이토 피자", "XL", 25.00);
장점
  • 객체를 처음 만들 때 멤버변수를 바로 초기화할 수 있다는 장점이 있다.
  • 딱히 없는 것 같다.
단점
  • 인자가 추가되거나 삭제되면 코드의 수정 또는 삭제가 어렵다.
  • 코드의 가독성이 떨어지며, 특히 인자의 수가 많을 때에는 각 부분이 어떤 인자를 의미하는지 알기 어렵다.
결론

가변성이 있는 객체에 사용하기엔 적절하지 않은 방법.


Setter

Setter를 이용해 객체를 만드는 방법은 다음과 같다.(JavaBeans Pattern)

Pizza potatoPizza = new Pizza();
potatoPizza.setMenuName("포테이토 피자");
potatoPizza.setSize("XL");
potatoPizza.setPrice(25.00);
장점
  • 각 인자의 의미를 파악하기 쉽다.
  • 멤버 변수의 추가, 삭제에 대응이 용이하다.
단점
  • 객체 일관성이 깨질 수 있다 -> 초기에 정한 값이 언제든 바뀔 수 있다.
  • setter 메소드 때문에 immutable 클래스를 만드는 데에는 적절하지 않다.
    • Thread Safe를 위해 생성자 패턴보다 많은 일을 해야 한다. (객체 생성 및 초기 할당을 여러 번 해야한다는 뜻.)

Builder

Builder 패턴을 이용해 객체를 만드는 방법은 다음과 같다.

public class Pizza {
    private final String menuName; // 필수
    private final double price;    // 선택
    private final String size;     // 선택
    private final String dough;    // 선택

    public static class Builder {
        // 필수 인자들.
        private final String menuName; // 필수

        // 선택적 인자들 -> 기본 값으로 초기화를 해주어야 한다.
        private double price = 20.00;    
        private String size = "M";
        private String dough = "Original";

        public Builder(String menuName){
            this.menuName = menuName;
        }

        public Builder price(double price){
            this.price = price;
            return this;
        }
        public Builder size(String size){
            this.size = size;
            return this;
        }
        public Builder dough(String dough){
            this.dough = dough;
            return this;
        }
        public Pizza build(){
            return new Pizza(this);
        }
    }

    private Pizza(Builder builder){
        this.menuName = builder.menuName;
        this.price = builder.price;
        this.size = builder.size;
        this.dough = builder.dough;
    }
}

클래스 구조가 조금 복잡하지만 다음과 같이 사용이 가능하다.

Pizza potatoPizza = new Pizza
                            .Builder("포테이토")
                            .price(25.00)
                            .size("XL")
                            .dough("Napoli")
                            .build();
장점
  • 각 인자가 어떤 의미인지 알기 쉽다.
  • immutable 한 객체를 만들 수 있다.
  • 객체 일관성이 깨지지 않는다.
  • build() 함수를 통해 잘못된 값이 입력되었는지 검증도 가능하다.

인터페이스를 사용한다면 조금 더 유연하게 빌더 패턴의 사용이 가능하다.

public interface Builder<T>{
    public T build();
}

// Pizza의 빌더클래스가 위의 인터페이스를 구현
Builder<Pizza> builder = new Builder<>();

Pizza potatoPizza = builder("포테이토")
                            .price(25.00)
                            .size("XL")
                            .dough("Napoli")
                            .build();

참고