1. 생성자 대신 static factory method 고려하기.
Boolean으로 예시를 들어보자면 아래와 같다!
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
클래스가 클라이언트에게 static factory method를 생성자 대신 또는 같이 제공 할 수 있다.
장점
1. 이름이 있다! 메소드가 이해하기 쉬움
- BigInteger(int, int, Random) < BigInteger.probablePrime
2. 새 오브젝트를 만들 필요 없다.
- immutable 클래스의 경우 이미 생성된 인스턴스를 다시 사용할 수 있다
- 불필요하게 중복된 인스턴스들이 생성되는 것을 방지하기 위해 이미 생성된 인스턴스를 저장했다가 반복 사용 가능.
- Flyweight 패턴과 유사 : 객체 생성시 자원/시간이 많이 들면 프로그램 성능 크게 향상시킬 수 있다
- 여러번 호출되어도 이미 생성된 동일 객체를 반환하기 때문에 클래스에서 언제든지 인스턴스 존재를 직접 제어할 수 있다 (instance-controlled class)
- instance-controlled class 작성 이유.
- 싱글톤(singleton) 또는 인스턴스 생성 불가(noninstantiable) 클래스로 만들 수 있음
- 불변 클래스에서 두 개의 동일한 인스턴스가 생기지 않도록 해줌
- 인스턴스의 동일 여부 확인할 때 equals(Object) 대신 == 연산자 사용 가능
- instance-controlled class 작성 이유.
3. subtype을 리턴타입으로 할 수 있다.
- public으로 선언하지 않은 클래스 객체를 반환하는 api를 만들 수 있다.
- implementation을 숨김으로서 api를 작게만들 수 있다. ( Item 20. interface-based framework )
-
public static factory method가 반환하는 클래스가 public일 필요가 없어 인자로 어떤 클래스의 객체를 만들지 동적으로 결정 가능.
- java8 이전에는 interface가 static method를 가지지 못했다.
관습에 따라 Type이라는 인터페이스의 정적 팩토리 메소드는 Types라는 noninstantiable companion 클래스에 넣어졌습니다.
Collections에는 45개의 utility 인터페이스 중 대부분은 noninstantiable companion 클래스(java.util.Collections)에 있는 static 팩토리 메소드를 통해서 객체를 생성하는데, 이 때 반환되는 객체의 클래스들은 모두 public이 아니다. - 컬렉션 프레임워크 API는 규모가 매우 작은데, 이를 구현하는 클래스를 45개의 public 클래스로 따로 사용해야 했다면 규모가 커졌을 것 API 사용자 입장에서는 반환되는 객체가 해당 컬렉션 인터페이스에 지정된 API를 갖는다는 것만 알면 되는데 API를 구현하는 클래스를 상세히 알 필요가 없음
- static 팩토리 메소드를 사용해 객체를 생성하므로 반환 객체의 타입을 참조할 때 컬렉션의 구현 클래스 별로 따로 참조하지 않고 인터페이스 타입으로 참조 하도록 요구한다.
static 팩토리 메소드를 갖는 클래스를 작성하는 시점에 메소드로부터 반환되는 객체의 클래스가 존재하지 않아도 됨
자바 데이터베이스 연결 API와 같은 서비스 제공자 프레임워크(service provider framework)의 근간 - java8 변경사항
java8에서 인터페이스에 정적 메소드를 포함할 수 있게 되어 인터페이스에 public static member들을 넣어야합니다.
그러나 java8에서 모든 static method를 public으로 만들어야 하기 때문에 구현 코드들을 별도의 private class에 넣어줘야 할 수도 있습니다.
java9에서는 private static method를 허용하지만 static fields 와 static member classes는 public으로 만들어야 합니다
4. object를 반환하는 클래스 함수의 parameter로 불릴때의 다양성
5. 메소드를 포함한 클래스 생성시에 반환된 객체가 존재할 필요 없다.
- service provider framework의 기본 마인드!
- 구성 Compenent
- Service Interface : Service 사용자에게 제공하기 위해 표준?으로 정한 API가 정의된 Interface
- Service Registration API : Provider Interface의 구현체를 등록하는 API
- Service Access API : Service Interface의 구현체를 얻어오는 API (정적 팩토리 메소드1)
- Provider Interface : Service Interface의 하위 객체를 생성해주는 API가 정의된 Interface
// 1) Service Interface
public interface Service {
...
}
// 4) Provider Interface
public interface Provider {
Service newService();
}
// 팩토리 클래스 & 객체생성불가능 클래스
public class Services {
private static final Map<String, Provider> providersMap = new ConcurrentHasshMap<String, Provider>();
private Services() {
} // 객체생성방지
// 2) Service Registration API
public static void registerProvider(String name, Provider provider) {
providersMap.put(name, provider);
}
// 3) Service Access API (정적 팩토리 메소드)
public static Service getMyService(String name) {
Provider provider = proidersMap.get(name);
if (provider != null) {
return provider.newService();
} else {
throw new IllegalArgumentException("No Provider");
}
}
}
단점
1. 인스턴스 생성을 위해 static 팩토리 메소드만 갖고 있는 경우, public이나 protected 생성자가 없는 클래스는 서브 클래스를 가질 수 없다.
2. 프로그래머가 찾기 힘들다 ( 다른 static 메소드와 쉽게 구별할 수 가 없음 )
- 해결책 : 주석을 통해 static factory method라는것을 알려야 한다
- common names
- from - 타입변경
- of - 집합
- valueof - from과 of의 합
- getInstance - 파라미터를 적용한 instance를 반환
- create or newInstance - getInstance와 같지만 매번 새 객체 반환
- getType - getInstance와 같지만 다른 클래스에서 사용됨
- newType - newInstance와 같지만 다른 클래스에서 사용됨
- type - getType과 newType의 합
요약
static factory method와 public constructor가 각 사용법이 있다. 무조건 static factory method를 쓰지말고 잘 생각해서 쓸 것
참고
- https://johngrib.github.io/wiki/static-factory-method-pattern/
- http://hahaha333.egloos.com/3901991
- http://casek.tistory.com/entry/Effective-Java-2%EC%9E%A5-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%83%9D%EC%84%B1%EA%B3%BC-%EC%82%AD%EC%A0%9C?category=574307
Comments