Effective Java - Item 1
Updated:
Effective Java
Item 1. 생성자 대신 정적 팩토리 메소드를 고려하라
클라이언트가 클래스의 인스턴스를 얻는 수단
2가지의 방법이 있다.
- public 생성자
- 정적 팩터리 메서드(static factory method)
정적 팩터리 메서드의 예
public class WrapperEx01 {
public static void main(String[] args) {
int i = 10;
// 박싱(int → Integer)
Integer wi = new Integer(i); // public 생성자를 이용
Integer wi = Integer.valueOf(i); // Static Factory Method를 이용
}
}
위 메소드는 기본 타입인 int값을 받아 Integer 객체 참조로 변환하였다.
이를 박싱클래스 라고 한다.
정적 팩토리 메소드의 장점
-
이름을 가질 수 있다.
전통적으로 public 생성자는 생성자의 이름과 매개변수 만으로는 객체의 특성을 제대로 설명 할 수 없다.
또한, 하나의 시그니처로는 생성자 하나만 만들 수 있다.
위 문제를 해결하기 위해,
정적 팩토리 메소드
는 이름을 다르게 작성하여 반환될 객체의 특성을 쉽게 묘사할 수 있으며,
한 클래스에 시그니처가 같은 생성자가 여러 개 필요할 것 같으면, 각각 차이를 잘 드러내는 이름을 지어 구별 할 수있다. - 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.
불변 클래스(immutable class)는 인스턴스를 미리 만들어 놓거나, 새로 생성한 인스턴스를 캐싱하여 불필요한 객체 생성 방지 가능
Integer.valueOf(int)
메소드는 객체를 아예 생성하지 않음.
위 방법을 통해, 반복되는 요청에 같은 객체를 반환하는 식으로 인스턴스 수명을 통제할 수 있다.
이런 클래스를 인스턴스 통제 클래스라 한다.
통제하는 이유?- 싱글턴(singleton)
- 인스턴스화 불가(noninstantiable)
- 불변 값 클래스에서 동치인 인스턴스가 단 하나뿐임을 보장할 수 있다.
- 플라이웨이트 패턴의 근간이된다.
- 열거 타입은 인스턴스가 하나만 만들어짐을 보장
-
반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.
위 능력은 반환할 객체의 클래스를 자유롭게 선택할 수 있는유연성
을 제시한다.
API를 만들 때 이 유연성을 응용하면 구현 클래스를 공개하지 않고도 그 객체를 반환할 수 있어 API를 작게 유지 할 수 있다.
Collection 프레임워크는 45개의 유틸리티 구현체를 제공하는데, 이 구현체 대부분을 단 하나의 인스턴스화 불가 클래스인java.util.Collections
에서 정적 팩터리 메서드를 통해 얻도록 했다. 따라서 API가 작아진 것은 물론 개념적인 무게(API를 사용하기 위해 익혀야 하는 개념 수와 난이도)를 낮추었다. - 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
반환 타입의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하든 상관없다.
EX) EnumSet Class의 경우- 원소가 64개 이하이면 -> RegularEnumSet
- 원소가 65개 이상이면 -> JumboEnumSet
인스턴스를 반환한다.
따라서 클라이언트는 Factory가 건네주는 객체가 어느클래스의 인스턴스인지 알 필요 없다. EnumSet의 하위 클래스이기만 하면 된다.
- 정적 팩터리 메서드를 작성하는 시점에는 반환할 개게의 클래스가 존재하지 않아도 된다.
구체적인 클래스 객체를 반환하지 않고, 인터페이스를 반환할 수 있다.
이런 유연함은 서비스 제공자 프레임 워크를 만드는 근간이 되며, 대표적인 서비스 제공자 프레임워크로는 JDBC(Java Database Connectivity)가 있음.
서비스 제공자 프레임워크
3개의 핵심 컴포넌트
- 서비스 인터페이스 (Service Interface) : 구현체의 동작을 정의
- 제공자 등록 API (Provider Registration API) : 제공자가 구현체를 등록
- 서비스 접근 API (Service Access API) : 클라이언트가 서비스의 인스턴스를 얻을 때 사용
- 서비스 제공자 인터페이스 (service provider interface) : 추가 컴포넌트, 서비스 인터페이스의 인스턴슬르 생성하는 팩터리 객체를 설명
정적 팩토리 메소드의 단점
-
정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.
public 이나 protected 생성자가 존재하지 않으면, 하위 클래스를 생성할 수 없음.
따라서 Collection 프레임워크의 유틸리티 구현 클래스들은 상속 할 수없음.
하지만 이 제약은 상속보다 컴포지션을 사용하도록 유도하면 오히려 장점으로 받아드릴 수 있음. -
프로그래머가 찾기 힘들다.
생성자와 같이 API 설명에 명확히 드러나지 않아, 익숙하지 않은 사용자는 정적 팩터리 메서드 방식 클래스를 인스턴스화할 방법을 알아내야 한다.
정적 팩터리 메서드에 흔히 사용하는 명명 방식
- from : 매개변수를 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메서드
- of : 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드
- valueOf : from과 of의 더 자세한 버전
- instance : 혹은 getInstance, 매개변수로 명시한 인스턴스를 반환하지만, 같은 인스턴스임을 보장하지는 않음
- create : 혹은 newInstance, instance와 같지만, 매번 새로운 인스턴슬르 생성해 반환
- getType : getInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 사용
- newType : newInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 사용
- type : getType과 newType의 간결한 버전
정리
정적 팩터리 메서드와 public 생성자는 각자 쓰임새가 있으나, 정적 팩터리를 사용하는 게 유리한 경우가 더 많으므로 무작정 public 생성자를 제공하던 습관을 고치자
출처
Joshua Bloch. Effective Java 3/E. n.p.: 인사이트, 2018년 11월 1일.