정적 팩토리와 생성자는 선택적 매개변수가 많을 때 적절히 대응하기가 어렵다.
이를 해결하기위한 몇 가지 대안을 알아보자
점층적 생성자 패턴(telescoping constructor pattern
필수 매개변수와 선택적 매개변수를 조합하여 여러 개의 생성자를 만들어서 사용하는 방식
단점 : 점층적 생성자 패턴도 쓸 수는 있지만, 매개변수 개수가 많아지면 클라이언트 코드를 작성하거나 읽기 어려워진다.
자바빈즈 패턴(JavaBeans Pattern)
매개변수가 없는 생성자로 객체를 만들고, setter 메서드를 사용하여 매개변수의 값을 설정하는 방식
단점
- 객체를 하나 만드는데 여러 개의 메서드를 호출해야 한다.
- 객체가 완전히 생성되기 전까진 일관성(consistency)이 무너진 상태에 놓이게 된다.
- 일관성 문제로 인해 클래스를 불변으로 만들 수 없다.
위와 같은 단점을 완화하고자 객체를 freezing 하는 방법이 있지만, 객체 사용 전에 freeze 메서드를 개발자가 호출했는지 컴파일러가 보증할 수 없어 런타임 오류에 취하다.(실전에서는 거의 사용되지 않는 방법)
빌더 패턴(Builder pattern)
빌더 패턴 : 점층적 생성자 패턴의 안전 + 자바 빈즈 패턴의 가독성
- 클라이언트는 필수 매개변수만으로 생성자 또는 정적 팩토리 메서드를 호출하여 빌더 객체를 얻는다.
- 빌더 객체가 제공하는 일종의 세터 메서드들로 원하는 선택 매개변수를 설정한다.
- 매개변수가 없는 build메서드를 호출하여 (보통은 불변인) 객체를 얻는다.
플루언트 API(Fluent API), 메서드 연쇄(method chaining) : 빌더 자신을 반환하는 세터 메서드 -> 연쇄적으로 호출 가능
유효성 검사 : 빌더의 생성자와 메서드에서 입력 매개변수 검사, build 메서드가 호출하는 생성자에서 여러 매개변수의 불변식(invariant) 검사
참고
- 불변(immutable 또는 immutability) : 어떠한 변경도 허용하지 않는다.(<-> 가변(mutable))
- 불변식(invariant) : 변경을 허용할 수는 있지만, 주어진 조건 내에서만 허용한다.
- 불변은 불변식의 극단적인 예
빌더 패턴은 계층적으로 설계된 클래스와 함께 사용하기에 좋다.
계층적으로 설계된 클래스의 빌더 패턴 예제( Pizza, NyPizza, Calzone)
Pizza.Builder 클래스는 재귀적 타입 한정(아이템 30)을 이용하는 제네릭 타입이다.
시뮬레이트한 셀프 타입(simulated self type) : self타입이 없는 자바를 위해 추상 메서드인 self를 더해 하위 클래스에서 형변환하지 않고도 메서드 연쇄를 지원한다.
공변 반환 타이핑(convariant return typing) : 하위 클래스의 메서드가 상위 클래스의 메서드가 정의한 반환 타입이 아닌, 그 하위 타입을 반환하는 기능( NyPizza.Builder는 NyPizza를 반환, Calzone.Builder는 Calzone을 반환)
빌더를 이용하면 가변인수(varargs) 매개변수를 여러 개 사용할 수 있다.
- 여러 개의 매개변수를 각각을 적절한 메서드로 나눠 선언하거나, 메서드를 여러 번 호출하도록 하고 각 호출 넘겨진 매개변수를 하나의 필드로 모으거나
- 예 : addTopping 메서드
빌더 패턴의 단점
- 빌더 생성 비용이 크지는 않지만 성능이 민감한 상황에서는 문제가 될 수 있다.
- 매개변수가 4개 이상은 되어야 값어치를 한다.
하지만 API는 시간이 지날수록 매개변수가 많아지는 경향이 있다. 그렇기 때문에 애초에 빌더로 시작하는 편이 나을 때가 많다.
정리
생성자나 정적 팩토리가 처리해야 할 매개변수가 많다면 빌더 패턴을 선택하는 게 더 낫다.
빌더 패턴은 점층적 생성자보다 클라인트 코드를 읽고 쓰기가 훨씬 간결하고, 자바빈즈보다 훨씬 안전하다.
'책읽고 정리 > Effective Java' 카테고리의 다른 글
[Effective Java 3/E] 아이템6. 불필요한 객체 생성을 피하라 (0) | 2021.06.10 |
---|---|
[Effective Java 3/E] 아이템5. 자원을 직접 명시하지 않고 의존 객체 주입을 사용하라 (0) | 2021.05.29 |
[Effective Java 3/E] 아이템4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2021.05.25 |
[Effective Java 3/E] 아이템3. private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2021.05.24 |
[Effective Java 3/E] 아이템1. 생성자 대신 정적 팩터리 메서드를 고려하라 (0) | 2021.05.11 |
댓글