클라이언트가 클래스의 인스턴스를 얻는 방법 중 한가지로 생성자와 별도로 정적 팩터리 메서드(static factory method)를 제공하는 방법이 있다. (그 클래스를 반환하는 단순한 정적 메서드)
boolean 값을 받아 Boolean 객체 참조로 변환해 준다.
public static Boolean valueOf(boolean b){
return b ? Boolean.TRUE : Boolean.FALSE;
}
정적 팩터리를 사용했을 때 생성자 보다 좋은점과 나쁜점을 알아보자.
장점
첫 번째, 이름을 가질 수 있다.
정적 팩터리는 이름만 잘 지으면 반환될 객체의 특성을 쉽게 묘사할 수 있다.
BigInteger (int bitLength, int certainty, Random rnd) - 생성자
BigInteger.probablePrime (int bitLength, Random rnd) - 정적 팩터리 메서드
위의 두 코드를 봤을 때 값이 소수인 BigInteger를 반환한다.
라는 의미를 더 잘 설명하는 메서드를 선택하라고 하면 생성자보다 정적 팩터리메서드를 사용한 코드일 것이다.
두 번째, 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.
불변 클래스(immutable class)는 인스턴스를 미리 만들어 놓거나 생성한 인스턴스를 캐싱하여 재활용하는 식으로 불필요한 객체 생성을 피할 수 있다.
예) Boolean.valueOf(boolean) 이 메서드는 객체를 생성하지 않고 미리 만들어져있던 불변 객체를 리턴하게 된다. 같은 객체가 자주 요청되는 상황(특히 생성비용이 큰)이라면 이런 방식을 많이 사용하는데 플라이웨이트 패턴(Flyweight pattern)도 이와 비슷한 기법이라 할 수 있다.
세 번째, 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.
이 능력은 반환할 객체의 클래스를 자유롭게 선택할 수 있게 하는 '엄청난 유연성'을 선물한다. API를 만들 때 이 유연성을 응용하면 구현 클래스를 공개하지 않고도 그 객체를 반환할 수 있어 API를 작게 유지할 수 있다. 이는 인터페이스를 정적 팩터리 메서드의 반환 타입으로 사용하는 인터페이스 기반 프레임워크를 만드는 핵심 기술이기도 하다.
네 번째, 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
반환 타입의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하든 상관 없다.
가령 EnumSet 클래스는 public 생성자 없이 오직 정적 팩터리만 제공하는데, OpenJDK에서는 원소의 수에 따라 두 가지 하위 클래스 중 하나의 인스턴스를 반환한다. 원소가 64개 이하면 원소들을 long 변수 하나로 관리하는 RegularEnumSet, 65개 이상이면 long 배열로 관리하는 JumboEnumSet의 인스턴스를 반환한다.
다섯 번째, 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
이런 유연함은 서비스 제공자 프레임워크(service provider framework)를 만드는 근간이 된다.
⇒ 대표적인 서비스 제공자 프레임워크 : JDBC (Java Database Connectivity)
여기서 서비스 제공자 프레임워크에 대해 간단히 알아보자.
서비스 제공자 프레임워크는 3개의 핵심 컴포넌트로 이루어진다.
- 서비스 인터페이스(service interface) : 구현체 동작을 정의
- 제공자 등록 API(provider registration API) : 제공자가 구현체를 등록할 때 사용
- 서비스 접근 API(service access API) : 클라이언트가 서비스의 인스턴스를 얻을 때 사용
클라이언트는 서비스 접근 API를 사용할 때 원하는 구현체의 조건을 명시할 수 있다. 조건을 명시하지 않으면 기본 구현체를 반환하거나 지원하는 구현체들을 하나씩 돌아가며 반환한다. 이 서비스 접근 API가 바로 서비스 제공자 프레임워크의 근간이라고 한 '유연한 정적 팩터리'의 실체다.
JDBC
- Connection이 서비스 인터페이스 역할
- DriverManager.registerDriver가 제공자 등록 API 역할
- DriverManager.getConnection이 서비스 접근 API 역할(유연한 정적 팩터리)
- Driver가 서비스 제공자 인터페이스 역할
// Oracle 드라이버 등록 (보통 Class.forName(oracle.jdbc.driver.OracleDriver)을 사용함)
DriverManager.registerDrivers(new oracle.jdbc.driver.OracleDriver());
Connection connection = DriverManager.getConnection(url, id, pw);
- 제공자 등록 API(DriverManager.registerDriver)를 이용해서 드라이버를 등록한다.
- 서비스 접근 API(getConnection)을 이용해서 서비스 인터페이스(Connection)을 얻어온다.
단점
첫 번째, 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.
두 번째, 정적 팩터리 메서드는 프로그래머가 찾기 어렵다.
생성자차럼 API설명에 명확히 드러나지 않으니 사용자는 정적 팩터리 메서드 방식 클래스를 인스턴스화할 방법을 알아내야 한다.
정적 팩터리 메서드에 흔히 사용하는 명명방식들
- from
- of
- valueOf
- instance 혹은 getInsrance
- create 혹은 newInstance
- getType
- newType
- type
'프로그래밍 노트 > Effective 시리즈' 카테고리의 다른 글
[Effective Kotlin. 26] 함수 내부의 추상화 레벨을 통일하라 (0) | 2023.10.25 |
---|---|
[Effective Kotlin. 36] 상속보다는 컴포지션을 사용하라 (0) | 2023.10.17 |
자원을 직접 명시하지 말고 의존 객체 주입을 사용하자 (0) | 2020.01.18 |
옵셔널(Optional)을 반환하는 메서드 설계 (0) | 2020.01.15 |
메서드 시그니처를 신중히 설계하자 (API 설계 요령) (0) | 2020.01.12 |