JAVA/이펙티브 자바 3

2장 아이템2 - 생성자에 매개변수가 많다면 빌더를 고려하라

lovineff 2021. 4. 2. 09:37

핵심 정리

생성자나 정적팩터리가 처리해야할 매개변수가 많다면 빌더 패턴을 선택하는게좋다.
매개변수 중 다수가 필수가 아니거나 같은 타입이면 특히 더 그렇다. 
빌더는 점층적 생성자 보다 클라이언트 코드를 읽고 쓰기가 훨씬 간결하고, 자바빈즈보다 월씬 안전하다.

 

점층적 생성자 패턴 (Telescoping Constructor Pattern)

매개변수 개수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다

public class NutntionFacts {
	private final int servingSize;
	private final int servings;
	private final int calories;
	private final int fat;

	public NutntionFacts(int servingSize, int servings) { ... }
	public NutntionFacts(int servingSize, int servings, int calories) { ... }
	public NutntionFacts(int servingSize, int servings, int calories, int fat) { ... }
}

 

자바빈즈 패턴(JavaBeans Pattern)

매개변수가 없는 생성자로 객체를 만든 후, 세터(setter) 메서드들을 호출해 원하는 매개변수의 값을 설정하는 방식

객체 하나를 만들려면 메서드를 여러 개 호출해야 하고, 객체가 완전히 생성되기 전까지는 일관성(consistency)이 무너진 상태에 놓이게 된다

public class Test {
    private int servingSize;
    private int servings;
    private int calories;
    private int fat;

    public Test() {}
    public void setServingSize(int servingSize) {this.servingSize = servingSize;}
    public void setServings(int servings) {this.servings = servings;}
    public void setCalories(int calories) {this.calories = calories;}
    public void setFat(int fat) {this.fat = fat;}
}

 

빌더 패턴(BuilderPattern)

Test 클래스는 불변, 쓰기 쉽고, 읽기 쉽다.

빌더 하나로 여러 객체를 순회하면서 만들 수 있고, 빌더에 넘기는 매개변수에 따라 다른 객체를 만들 수도 있다

public class Test {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;

    public static class Builder{
        private int servingSize;
        private int servings;
        private int calories;
        private int fat;

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

        public Builder servingSize(int val){
            servingSize = val;
            return this;
        }

        public Builder servings(int val){
            servings = val;
            return this;
        }

        public Builder calories(int val){
            calories = val;
            return this;
        }

        public Builder fat(int val){
            fat = val;
            return this;
        }

        public Test build(){
            return new Test(this);
        }
    }

    private Test(Builder builder){
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
    }
}

Test test = new Test.Builder().servingSize(1).servings(2).calories(3).fat(4).build();

 

추가 내용

lombok을 사용하는 경우 Builder 클래스 구현은 @builder 어노테이션으로 대체 가능하다.