반응형

본 게시글은 Effective Java 2판을 참고하여 작성하였습니다.


생성자 인자가 많다면 코드작성 뿐만아니라 가독성도 떨어지게 될 것입니다. 보통 선택적 인자를 받는 생성자가 존재할 때는 점층적 생성자 패턴을 적용합니다. 필수 인자만 받는 생성자를 하나 정의하고, 선택적 인자를 하나씩 추가해서 작성하곤 합니다. 만약 그 선택적 인자가 10개 이상이 넘어간다면 그 많은 인자가 무슨 값인지 알기 위해서는 주의 깊게 봐야합니다. 또한 자료형이 같은 인자들이 많아진다면 클라이언트가 인자의 순서를 실수로 뒤집게 되는 경우 문제가 생기겠죠


때문에 두번째 대안으로 자바빈 패턴을 이용하곤 합니다. 인자 없는 생성자를 호출하여 객체를 생성한 다음, 설정 메서드를 호출하여 필수 필드와 선택필드까지 초기화 하는 것입니다. 하지만 1회의 함수 호출로 객체 생성을 끝낼 수 없어, 객체 일관성이 일시적으로 깨질수 있다는 단점이 존재하죠. 일관성이 깨지면 디버깅 하기도 어렵고 자바빈 패턴으로는 변경 불가능한 클래스를 만들 수 없다는 것이 가장 critical하다고 할 수 있습니다. 


그렇다면 이러한 문제점을 해결할 수 있는 방법은 무엇일까요? 바로 Builder패턴입니다. 필요한 객체를 직접 생성하는 대신, 먼저 필수 인자들을 생성자 or 정적 팩토리 메서드(1강 참조)에 전부 전달하여 빌더 객체를 만듭니다. 이후 빌더 객체에 정의된 설정 메서드들을 호출하여 선택적 인자를 추가하는 방식입니다. 마지막으로는 아무런 인자없이 build메서드를 호출하여 변경 불가능한 객체를 만듭니다. 음... 일단 그 실제 코드를 볼까요?


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 value) {

calories = value;

return this;

}


public Builder fat(int value) {

fat = value;

return this;

}


public Builder carbohydrate(int value) {

carbohydrate = value;

return this;

}


public Builder sodium(int value) {

sodium = value;

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 객체가 변경 불가능하다는 것과 모든 인자가 한곳에 모여 있다는 것을 다시 보자. 빌더에 정의된 설정 메서드는 빌더 객체 자신을 반환하기 때문에 코드는 이어서 쓸 수 있다. 사용하는 예를 보자.


NutritionFacts coke = new NutritionFacts.Builder(240,8). caloreis(100).sodium(35).carbohydrate(27).build();


어떤가?? 필자는 이 코드를 보고 아니 이 패턴을 보고 감동했다. 마치 Python과 같이 선택적 인자에 이름을 붙일 수 있는 방법과 비슷하지 않은가? 아니 실례로 현업에서 지저분한 소스 코드를 봤던 필자에게는 마치 감동일 수 밖에 없었다. 알아보기 쉽고 쓸데없는 주석도 필요없다. 마지막 build 메서드를 통해 해당 불변식이 위반되었는지 검사 할수 있으며 실제 객체의 필드를 두고 검사할 수 있다는 것도 매우 중요하다.

그리고 빌더 패턴은 유연하다. 하나의 빌더 객체로 여러 객체를 만들 수 있기 때문이다. 다른 객체를 생성해야 할 때 마다 빌더 객체의 설정 메서드를 호출하면 다음 생성 객체를 바꿀수 있으며, 어떤 필드의 값은 자동으로 채울 수도 있다!


하지만 이 역시 장점만 존재할 수는 없을 것이다.(참 아쉽다) 객체를 생성하려면 우선 빌더 객체를 생성해야 한다. 성능에서는 분명 차이가 있겠지만 실무에서는 큰 문제가 될 소지는 아니라고 판단된다. 두번째는 이 빌더 패턴을 구현하는 소스는 점층적 생성자 패턴보다 많은 코드를 요구하기 때문에 인자가 충분히 많은 상황에서 이용해야 한다.(통제하기 힘들 정도 필자는 10개 이상이라고 생각한다) 


앞 장에서 작성한 정적 팩토리 메서드도 빌더 패턴도 상황에 맞게 써야 할 것이다. 이번장에서 작성한 빌더 패턴은 인자가 많은 생성자(특히 선택적 인자)나 정적 팩토리가 필요한 클래스를 설계할 때 매우 유리할 것이다. 가독성은 편해지고 자바빈 패턴보다 안전한 것은 물론이다. 모든 상황에 맞는 패턴은 존재하지 않는다. 다양한 패턴을 익혀두고 상황에 맞는 패턴으로 구현하는 자세를 가져야 겠다.

반응형

+ Recent posts