01. 생성자 대신 정적 패터리 메서드를 고려하라
클라이언트가 클래스의 인스턴스를 얻는 전통적인 수단은 public 생성자다. 클래스는 생성자와 별도로 정적 팩터리 메서드(static factory method)
를 제공할 수 있다.
정적 팩터리 메서드가 생성자 보다 좋은 장점
1. 이름을 가질 수 있다.
생성자에 넘기는 매개변수와 생성자 자체로는 반환될 객체의 특성을 제대로 설명할 수 없다.
하지만 정적 팩터리 메서드는 이름만 잘 짓는다면 반환될 객체의 특성을 쉽게 묘사할 수 있다. 생성자 BigInteger(int, int, Random)
과 정적 팩터리 메서드인 BigInteger.probablePrime
중에서, 소수인 BigInteger를 반환한다는 의미를 더 잘 표현하는 것은 후자이다.
한 클래스에 시그니처가 같은 생성자가 여러 개 필요한 경우엔, 생성자를 정적 팩터리 메서드로 바꾼 뒤, 각각의 차이를 잘 드러내는 이름을 지어주면 된다.
2. 호출될 때 마다 인스턴스를 새로 생성하지 않아도 된다.
이 장점을 통해 불변 클래스는 인스턴스를 미리 만들어 놓거나, 새로 생성한 인스턴스를 캐싱하여 재활용하는 식의 불필요한 객체 생성을 피할 수 있다. 대표적 예로, Boolean.valueOf(boolean)
메서드는 객체를 아예 생성하지 않는다. 생성비용이 큰 객체가 자주 요청되는 상황에서는 성능이 상당히 향상된다.
즉, 반복되는 요청에 같은 객체를 반환하는 식으로 정적 팩터리 방식의 클래스는 언제 어느 인스턴스를 살아있게 할 지를 철저하게 통제할 수 있다. 인스턴스를 통제하는 이유는, 인스턴스를 통제하므로써 클래스를 싱글턴으로 만들거나, 인스턴스화 불가로 만들 수 있기 때문이다.
3. 반환 타입의 하위 타입 객체를 반환할 수 있다.
API를 만들 때 이 유연성을 응용하면 구현 클래스를 공개하지 않고도 그 객체를 반환할 수 있기 때문에 API를 작게 유지할 수 있다 이는 인터페이스를 정적 팩터리 메서드의 반환 타입으로 사용하는 인터페이스 기반 프레임워크를 만드는 핵심 기술이기도 하다.
예컨대 자바 컬렉션 프레임워크는 핵심 인터페이스들에 수정 불가나 동기화 등의 기능을 덧붙인 총 45개의 유틸리티 구현체를 제공하는데, 이 구현체 대부분을 단 하나의 인스턴스화 불가 클래스인 java.util.Collections
에서 정적 팩터리 메서드를 통해 얻도록 했다.
컬렉션 프레임워크는 이 45개의 클래스를 공개하지 않기 때문에 API 외견을 훨씬 작게 만들 수 있었다. 프로그래머는 명시한 인터페이스대로 동작하는 객체를 얻을 것임을 알기에, 굳이 별도 문서를 찾아가면 실제 구현 클래스가 무엇인지 알아보지 않아도 된다. 나아가 정적 팩터리 메서드를 사용하는 클라이언트는 얻은 객체를 그 구현 클래스가 아닌 인터페이스만으로 다루게 된다.
4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
반환 타입의 하위 타입이기만 한다면 어떤 클래스의 객체를 반환하든 상관없다.
5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다. 나아가 이러한 유연함은 서비스 제공자 프레임워크
를 만드는 근간이 되는데, 대표적으로는 JDBC가 있다.
서비스 제공자 프레임워크 패턴에는 여려 변형이 있다. 브리지 패턴(Bridge Pattern)이나, 의존 객체 주입(DI) 등이 그 예이다.
단점
1. 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.
앞서 이야기한 컬렉션 프레임워크의 구현 클래스들은 상속할 수 없다는 이야기이다. 이 제약은 상속보다는 컴포지션을 사용하도록 유도하고, 불변 타입으로 만드려면 이 제약을 지켜야 한다는 점에서 오히려 장점으로 받아들일 수도 있다.
2. 정적 팩터리 메서드는 프로그래머가 찾기 어렵다.
API 문서를 잘 써놓고 메서드 이름도 널리 알려진 규약을 따라 짓는 식으로 문제를 완화할 수 있다.
-
from : 매개변수 하나를 받아 해당 타입의 인스턴스를 반환하는 형변환 메서드
ex)
Date d = Date.from(instance);
-
of : 여러 매개변수를 받아 적합한 타입으 인스턴스를 반환하는 집계 메서드
ex)
Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
-
valueOf : from과 of의 더 자세한 버전