Static Factory method의 예시장점1. 이름을 가질 수 있다2. 호출될 때마다 인스턴스를 새로 생성하지는 않아도 된다.3. 반환 타입(Interface로)의 하위 타입 객체(구현 클래스)를 반환할 수 있는 능력이 있다.4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 됨단점1. 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없음2. 정적 팩터리 메서드는 프로그래머가 찾기 어렵다
Static Factory method의 예시
- 참고로 디자인 패턴의 Factory Method가 아님!
Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING); BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE); StackWalker luke = StackWalker.getInstance(options); Object newArray = Array.newInstance(classObject, arrayLen); FileStore fs = Files.getFileStore(path); List<Complaint> litany = Collections.list(legacyLitany);
장점
1. 이름을 가질 수 있다
- 생성자 자체만으로는 반환될 객체의 특성을 제대로 설명하지 못함
- 정적 팩터리는 이름만 잘 지으면 반환될 객체의 특성을 쉽게 묘사 가능함
- 하나의 시그니처로는 생성자를 하나만 만들 수 있기에 다양한 용도로 객체를 생성하기 위해서 생성자를 여러 개 만들어야 하지만 이렇게 됐을 때, 각각의 생성자가 무엇을 위한 용도인지 알 방법이 없음
- 예 : BigInteger(int, int, Random) ↔ BigInteger.probablePrime() — 값이 소수인 BigInteger를 반환한다 는 의미 일때..
2. 호출될 때마다 인스턴스를 새로 생성하지는 않아도 된다.
- 인스턴스를 미리 만들어 놓거나 새로 생성한 인스턴스를 캐싱하여 재활용하는 식으로 불필요한 객체 생성을 피할 수 있음
- (생성 비용이 큰) 같은 객체가 자주 요청되는 상황이라면 위의 방식으로 성능을 많이 끌어 올릴 수 있음
- 플라이웨이트 패턴이 이와 비슷한 기법임
3. 반환 타입(Interface로)의 하위 타입 객체(구현 클래스)를 반환할 수 있는 능력이 있다.
- 자바 8 전에는 인터페이스에 정적 메서드를 선언할 수 없었기에,
Type
인 인터페이스를 반환하는 정적 메서드가 필요하면Types
라는 (인스턴스화 불가인) 동반 클래스(companion class)를 만들어 그 안에 정의하는 것이 관례였음 - java.util.Collection — java.util.Collections
- 자바 8 부터는 인터페이스가 정적 메서드를 가질 수 없다는 제한이 풀려서 companion class에 대한 필요가 별로 없음
- 그러나 여전히 package-private 한 클래스가 필요함
- 인터페이스에서 private 정적 메서드는 가능하지만, 정적 필드와 정적 멤버 클래스는 여전히 public 이어야 하기에.
4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다
- 반환 타입(Interface)의 하위 타입(구현체)이기만 하면 어떤 클래스의 객체를 반환하든 상관 없음
- 예시) EnumSet 클래스는 public 생성자 없이 정적 팩터리만 제공하는데, 원소의 수에 따라 두가지 하위 클래스 중 하나의 인스턴스를 반환함(RegularEnumSet(64개 이하), JumboEnumSet(65개 이상))
- 클라이언트는 위 예시의 두 가지 클래스의 존재를 모름
- 단지 EnumSet의 하위 클래스이기만 하면 됨
5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 됨
- 이러한 유연함이 서비스 제공자 프레임워크(service provider framework)를 만드는 근간이 됨
- 대표적인 서비스 제공자 프레임워크 : JDBC
- 서비스 제공자 프레임워크의 3개 핵심 컴포넌트
- 구현체의 동작을 정의하는 서비스 인터페이스 — JDBC 의 Connection
- 제공자가 구현체를 등록할 때 사용하는 제공자 등록 API — JDBC DriverManager.registerDriver
- 클라이언트가 서비스의 인스턴스를 얻을 때 사용하는 서비스 접근 API — JDBC DriverManager.getConnection
- 서비스 접근 API는 공급자(구현체. JDBC의 경우 MySQL, ... 등)가 제공하는 것보다 더 풍부한 서비스 인터페이스를 클라이언트에 반환할 수 있음. 브릿지 패턴이라 알려진 것
단점
1. 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없음
- java.util.Collections 는 상속할 수 없다는 의미
- 이 제약은 상속보다 컴포지션을 사용하도록 유도하고 불변 타입으로 만드려면 이 제약을 지켜야 한다는 점에서 오히려 장점으로 받아들일 수 있음
2. 정적 팩터리 메서드는 프로그래머가 찾기 어렵다
- API 설명에 명확히 드러나지 않으니 정적 팩터리 메서드 방식 클래스를 인스턴스화할 방법을 알아내야 함
- API 문서를 잘 써놓거나 메서드 이름을 널리 알려진 규약을 따라 짓는 식으로 문제를 완화해줘야 함
핵심 정리
정적 팩터리를 사용하는 게 유리한 경우가 더 많으므로 무작정 public 생성자를 제공하던 습관이 있다면 고치자.