블로그를 이전하였습니다. 2023년 11월부터 https://bluemiv.github.io/에서 블로그를 운영하려고 합니다. 앞으로 해당 블로그의 댓글은 읽지 못할 수 도 있으니 양해바랍니다.
반응형
1. 인스턴스를 얻는 방법
- 전통적인 방법은
public
생성자(constructor
) - 정적 팩토리 메소드(
static factory method
)로 부터 제공 받음- 그 클래스의 인스턴스를 반환하는 단순한 정적(static) 메소드
'정적 팩토리 메소드'는 '디자인 패턴'에서의 '팩토리 메소드(Factory Method
)'와 다르다. 또한, 디자인 패턴 중에 이와 일치하는 패턴은 없다.
정적 팩토리 메소드의 예
- 메소드 설명: 기본 타입(primitive type)인 boolean 값을 받아 Boolean 객체 참조로 변환
<java />
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
2. Static Factory Method 의 장점
2.1. 1. 이름을 가질 수 있다.
생성자(constructor
)만 이용해서는 반환될 객체의 특성을 묘사하기 힘들다.
반면, 정적 팩토리 메소드는 상대적으로 묘사하기 좋다.
예를들어,
<java />
// Constructor
BigInteger(int, int, Random)
// Static Factory Method
// - 값이 소수인 BigInteger를 반환한다.
BigInteger.probablePrime(int, Random) // 'Java 4'에 추가됨
한 생성자에 시그니터같은 생성자가 여러개가 필요할 것 같으면, (생성자를) 정적 팩토리 메소드로 바꾸고 차이를 잘 드러내는 이름으로 명시한다.
2.2. 2. 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.
- 불변 클래스(
immutable class>
)는 인스턴스를 미리 만들어 놓거나, 새로 생성한 인스턴스를 캐싱하여 재활용하여 불필요한 객체 생성을 피할 수 있다.- 예:
Boolean.valueOf(boolean)
메소드는 객체를 아이에 생성하지 않음
- 예:
- 생성 비용이 큰 객체가 자주 생성되는 경우에 사용하면 성능 향상에 좋다. (플라이 웨이트 패턴,
Flyweight Pattern
) - 인스턴스 통제(
instance-controlled
) 클래스: 언제 어느 인스턴스를 살아 있게 할지를 철저히 통제할 수 있다.- 통제의 이유: 싱글턴(
Singleton
) 또는 인스턴스화 불가(noninstantiable
)
- 통제의 이유: 싱글턴(
- 불변 값 클래스에서 인스턴스가 단 하나뿐임을 보장할 수 있다.
2.3. 3. 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.
- API를 만들때 구현 클래스를 공개하지 않아도, 그 객체를 반환할 수 있다. 그래서 API를 작게 유지할 수 있다.
- 예: 단 하나의 인스턴스 불가 클래스인
java.util.Collections
에서 정적 팩토리 메소드를 통해 얻어 올 수 있다.
- 예: 단 하나의 인스턴스 불가 클래스인
자바 컬렉션 프레임워크
- 핵심 인터페이스들에 '수정 불가'나 '동기화' 등의 기능을 덧붙인 총 45개의 유틸리티 구현체를 제공.
- 이를java.util.Collections
에서 정적 팩토리 메소드를 통해 얻을 수 있음
2.4. 4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
- 반환 타입의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하든 상관없다.
- 예를들어,
OpenJDK
에서는 원소의 수에 따라 두 가지 하위 클래스 중 하나의 인스턴스를 반환한다.
- 예를들어,
<python />
# pseudo code
if 원소 < 65:
return long 변수 하나로 관리하는 'RegularEnumSet'의 인스턴스
else if 원소 >= 65:
return long 배열로 관리하는 'JumboEnumSet'의 인스턴스
- 사용자는 RegularEnumSet이나 JumboEnumSet의 존재를 모른다.
- 이런식으로 입력 매개변수에 따라 다른 클래스의 객체를 반환하여 성능을 개선할 수 있다.
2.5. 5. 정적 팩토리 메소드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
- 이런 유연함으로 서비스 제공을 위한 프레임워크를 만들 수 있다.
- 대표적인 프레임워크로는
JDBC(Java Database Connectivity)
가 있다.- 제공자(provider)는 서비스 구현체이다.
- 이 구현체를 클라이언트에게 제공하는 역할을 프레임워크가 통제하여, 클라이언트로부터 구현체를 분리한다.
- 서비스 제공자 프레임워크의 3개의 핵심 컴포넌트
- 서비스 인터페이스(
service interface
): 구현체의 동작을 정의하는 인터페이스- 예: Connection
- 제공자 등록 API(
provider registration API
): 제공자가 구현체를 등록할 때 사용하는 API- DriverManager.registerDriver
- 서비스 접근 API(
service access API
): 클라이언트가 서비스를 얻을 때 사용하는 API
- DriverManager.getConnection
- 서비스 인터페이스(
3. Static Factory Method 의 단점
3.1. 1. 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩토리 메소드만 제공하면 하위 클래스를 만들 수 없다.
java.util.Collections
의 구현 클래스들은 상속할 수 없다는 얘기와 같다.
3.2. 2. 정적 팩토리 메소드는 프로그래머가 찾기 어렵다.
API처럼 설명이 명확히 드러나지 않아 사용자는 인스턴스화할 방법을 알아내야 한다.
자바독이 알아서 처리해줬으면 좋겠으나 아쉽게도 그날이 오기 전까지는 API 문서를 잘 작성해야 한다.
4. 정적 팩토리 메소드에 흔히 사용하는 명명 방식들
4.1. from
매개변수를 받아서, 해당 타입의 인스턴스를 반환하는 형변환 메소드
<java />Date d = Date.from(instant);
4.2. of
여러 매개변수를 받아서, 적합한 타입의 인스턴스를 반환하는 메소드
<java />Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
4.3. valueOf
from과 of의 더 자세한 버전
<java />BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
4.4. instance 또는 getInstance
매개변수로 명시한 인스턴스를 반환 (항상 같은 인스턴스임을 보장하지는 않음)
<java />StackWalker luke = StackWalker.getInstance(options);
4.5. create 또는 newInstance
매번 새로운 인스턴스를 생성해 반환함
<java />Object newArray = Array.newInstance(classObject, arrayLen);
4.6. getType
getInstance와 같으나, 생성할 클래스가 아닌 다른 클래스의 팩토리 메소드를 정의할때 사용.
<java />FileStore fs = Files.getFilesStore(path);
4.7. newType
newInstance와 같으나, 생성할 클래스가 아닌 다른 클래스의 팩토리 메소드를 정의할때 사용.
<java />BufferedReader br = Files.newBufferedReader(path);
4.8. type
getType과 newType의 간결한 버전
<java />List<Complaint> litany = Collections.list(lagacyLitany);
5. 정리
- 정적 팩토리 메소드와 public 생성자를 사용하는 방식이 있고, 각자의 쓰임새가 있으니 장단점을 이해하여 사용한다.
- 무작정 public 생성자를 제공하던 습관을 고치자.
6. Reference
- Effective JAVA
반응형