해당 글은 이펙티브 자바 (Effective Java 3/E - joshua bloch) 를 읽고 정리한 글입니다.
핵심
정적 팩터리 메서드와 public 생성자는 각자의 쓰임새가 있으니 상대적으로 장단점을 이해하고 사용할 것.
그러나 정적 팩터리를 사용하는 게 유리한 경우가 더 많으므로 무작정 public 생성자를 제공하던 습관은 고치자.
이유
1. 이름을 가질 수 있다.
- 반환될 객체의 특성을 쉽게 묘사
2. 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.
- 이로인해 불변 클래스(Immutable class)는 인스턴스를 미리 만들어 놓거나 새로 생성한 인스턴스를 캐싱하여 재활용하는 식으로 불필요한 객체 생성 피함
- 플라이웨이트 패턴(Flyweight pattern)과 비슷한 기법
- 인스턴스 통제(Instance-controlled)반복되는 요청에 같은 객체를 반환하는 식으로 정적 팩터리 방식의 클래스는 언제 어느 인스턴스를 살아 있게 할지를 통제 가능
3. 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.
- 반환 타입을 자유롭게 선택
- 응용하면 인터페이스 기반 프레임워크(item20)을 만드는 핵심기술 → 구현 클래스를 공개하지 않고도 그 객체 반환(API 작게 유지 가능)
public interface ExampleInterface {
ExampleResponse from(String arg);
}
- 이 interface만 알아도(구현 클래스를 보지 않더라도) 해당 클래스가 어떤 객체를 반환하는지 알 수 있다.
4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
- 반환 타입의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하든 상관없다.
5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
- JDBC같은 서비스 제공자 프레임워크(service provider framework)의 근간이 된다.
- 서비스 제공자 프레임워크(service provider framework) 핵심 컴포넌트
- 서비스 인터페이스(service interface)
- 구현체의 동작을 정의
- JDBC의 Connecton
- 제공자 등록 API(provider rgistration API)
- 제공자가 구현체를 등록할 때 사용
- JDBC의 DriverManager.registerDriver
- 서비스 접근 API(service access API)
- 클라이언트가 서비스의 인스턴스를 얻을 때 사용
- JDBC의 DriverManager.getConnection
- 서비스 제공자 인터페이스(service provider interface)
- 서비스 인터페이스의 인스턴스를 생성하는 팩터리 객체를 설명
- 없으면 각 구현체를 인스턴스로 만들 때 리플렉션(item 65)를 사용
- 서비스 인터페이스(service interface)
- 브리지 패턴(Bridge pattern)처럼 서비스 제공자 프레임워크 패턴에는 여러 변형 존재
고려 사항
1. 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.
- 컬렉션 프레임워크의 유틸리티 구현 클래스들은 상속할 수 없다.
2. 정적 팩터리 메서드는 프로그래머가 찾기 어렵다.
관련 코드 및 예시
from
: 매개변수 하나를 받아서 해당 타입의 인스턴스 반환- of: 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드
Set<Rank> faceCards = EnumSet.of(JACK, Queen, King);
valueOf
: from과 of의 더 자세한 버전instance
혹은 getInstance: (매개변수를 받는다면) 매개변수로 명시한 인스턴스를 반환하지만, 같은 인스턴스임을 보장하지 않는다.create
혹은newInstance
: instance 혹은 getInstance와 같지만, 매번 새로운 인스턴스를 생성해 반환함을 보장getType
: getInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 쓴다. “Type”은 팩터리 메서드가 반환할 객체의 타입이다.BufferedReader br = Files.newBufferedReader(path);
type
: getType과 newType의 간결한 버전List<Complaint> litancy = Collections.list(legacyLitancy);
'Develop > Java' 카테고리의 다른 글
Java | 인스턴스화를 막으려거든 private 생성자를 사용하라 (Effective Java 3/E - joshua bloch) (0) | 2024.07.04 |
---|---|
Java | private 생성자나 열거 타입으로 싱글턴임을 보증하라 (Effective Java 3/E - joshua bloch) (0) | 2024.07.04 |
Java | 생성자에 매개변수가 많다면 빌더를 고려하라 (Effective Java 3/E - joshua bloch) (0) | 2024.07.04 |
Java | Stream에 대하여 (0) | 2023.08.28 |
Java | interface와 abstract class차이에 대해서 설명해주세요. (0) | 2023.08.28 |