기존 방법 : Client → instance of Class 획득
→ public 생성자로 가져오기
public void main(String[] args){
Hello hello = new Hello(); // new Hello() -> 인스턴스 가져오기
hello = new Hello("xonmin"); // 다른 public 생성자
}
public class Hello{
//아무것도 생성되지 않으면 default Constructor
String name ;
public Hello(String name){
this.name = name;
}
}
정적 팩토리 메소드
예시
public static Boolean valueOf(boolean b){
return b ? Boolean.TRUE : Boolean.False;
}
우린 이제 일반 public 생성자만 고려하는 것이 아닌, 정적 팩토리 메소드를 고려해볼 수 있게 되자.
장점
1. 이름을 가질 수 있다 (즉 명확하게 네이밍을 해서 개발자가 좀 더 쉽게 받아들일 수 있게 한다.)
→ 객체의 특성을 묘사할 수 있다.
- 책 ex) BigInteger.probablePrime
- ex) Hello.SayToStudent()
p9 . line 1 - 하나의 시그니처로는 생성자 하나만 만들 수 있다.
따라서 이름이 같은 여러 생성자를 통해서 만들게 되면 다른 클래스에서 해당 클래스를 정의할 때, 매개변수를 넣는 것에 대한 불편함 (즉 API 참조가 불편)
Hello(String name, int age, Boolean shool)
Hello(String name, int age, String school)
하지만 이름을 다르게 할 수 있는 정적 팩토리 메서드에서는 이름으로 차이를 잘 드러나게 할 수 있기 때문에 이러한 문제 해결 가능
- 시그니처 : 메서드 명, 파라미터 순서, 타입 , 개수 를 포함
2. 호출될 때 마다 인스턴스를 새로 생성하지는 않아도 된다.
ex) Boolean.valueOf(boolean) 메서드는 객체 생성하지 않아도 static을 통해 사용 가능
대표적 방법 : 싱글턴 , 불변 값 클래스에서 같은 인스턴스는 오직 하나를 보장
따라서 객체 생성 비용이 큰 상황에서 자주 호출해야한다면 아주 효율적 → 인스턴스 통제 클래스
→ 이 덕분에 불변클래스(immutable class)를 미리 만들어 놓음 / 새로 생성한 인스턴스 캐싱 - 재사용 X
3. 반환 타입의 하위 타입 객체를 반환할 수 있다. → 유연성 보장
가장 큰 이점 : API를 작게 유지할 수 있음 (구현 클래스를 공개하지 않아도, 객체를 반환할 수 있음. 즉, 클래스의 전체 내용을 몰라도 됨)
- API가 작아지면 API를 사용하는 측에서 익혀야 하는 개념 (실제 구현 클래스가 뭔지 몰라도 됨)과 난이도를 낮출 수 있는 이점
ex) 명시한 인터페이스대로 동작하는 객체를 얻기 때문에 실제 구현 클래스가 뭔지 몰라도 됨 - 얻은 객체를 인터페이스만으로 다룰 수 있다
cf) 자바 8 부터 인터페이스도 정적 메서드를 가질 수 있기 때문에 인스턴스화 불가 동반 클래스를 둘 이유 X
동반 클래스에 두던 public static 멤버들을 그냥 인터페이스 안에 두면 된다.
하지만 interface는 public으로 명시하기 때문에 별도의 private 클래스에 두어야 하는 경우 존재
( 자바 9에서는 private 정적 메서드까지 허용 / but 정적 필드와 멤버 클래스는 여전히 public)
4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환 가능
반환 타입만 하위 타입 보장 ? 어떤 클래스의 객체를 반환해도 OK
클라이언트 입장에서는 반환되는 두 클래스의 존재도 몰라 하지만 알아서 인스턴스 객체 반환해주겠지 아니까 사용
ex)
EnumSet Class - public 생성자 X / Only 정적 팩토리 메서드 원소 수에 따라 하위 클래스 인스턴스 결정되어 반환
public static EnumSetClass(Object params){
if (params.size() <= 64 )
return RegularEnumSet Instance(long 변수 하나로 관리하는 )
else :
return JumboEnumSet Instance(long 배열로 관리하는)
}
5. 정적 팩토리 메서드를 작성하는 시점에서 반환할 객체의 클래스 존재하지 않아도 무방 (유연성 Up)
→ 이를 통해 Service Provider Framework 를 만들 수 있다
ex ) JDBC(Java Database Connectivity)가 있다.
서비스 제공자 프레임워크에서의 제공자(provider) = 서비스의 구현체
그리고 구현체들을 클라이언트에 제공하는 역할을 프레임워크가 통제하여, 클라이언트를 서비스 구현체로부터 분리해준다.
서비스 제공자 프레임워크의 구성요소
- 서비스 인터페이스
- 구현체 동작 정의
- ex) JDBC - Connection
- 제공자 등록 API
- 구현체 등록시 사용
- JDBC - DriverManager.registerDriver()
- 서비스 접근 API (유연성의 일등 공신)
- Client가 서비스의 인스턴스를 얻을 때 사용
- 클라이언트는 사용시 조건 명시 가능
- 이 때 조건을 명시하지 않는다면 default 구현체 return , 지원하는 구현체를 하나씩 돌아가며 반환(우선순위)
- ex..HttpMessageConverter..?
- JDBC - DriverManager.getConnection()
- (opt) 서비스 제공자 인터페이스
- 서비스 인터페이스의 인스턴스를 생성하는 팩토리 객체 설명 역할
- 없다면 각 구현체를 인스턴스로 만들 때 Reflection을 사용해야함
- ex JDBC - Driver
cf ) Reflection
자바에서 이미 로딩이 완료된 클래스에서 또는 다른 클래스를 동적으로 로딩하여 구체적인 타입을 알지 못하더라도 생성자, 멤버 필드, 멤버 메소드를 사용할 수 있는 기법
- 즉, 컴파일 타임이 아닌 런타임에 동적으로 특정 클래스의 정보를 객체화하여 분석 및 추출해낼 수 있는 프로그래밍 기법
- 사용 이유 : 런타임에 다른 클래스를 동적으로 로딩하여 접근할 필요가 있을 때
서비스 제공자 프레임워크 패턴의 변형된 버전
- 브릿지 패턴 (공급자가 제공하는 것보다 서비스 인터페이스를 더 풍부하게 클라이언트에게 반환 가능 )
- DI 프레임워크
단점
1. 상속을 하기 위해서는 public , protected 생성자가 필요하니 정적 펙토리 메서드만 제공하면 하위 클래스 생성 불가
but 상속이 아닌 컴포지션 사용을 장려, 불변 타입을 만들기 위해서는 이 제약을 지켜야하기 때문에 장점으로도 봄
2. 정적 팩토리 메서드는 개발자가 찾기 어렵다.
- 기본 생성자 처럼 API에 명확하게 명시된 것이 아니기 때문에, 사용하기 위해서는 정적 팩토리 메서드 방식 클래스를 스스로 인스턴스화하는 방법을 알아내야함
- 흔히 사용하는 정적 팩토리 메서드 명명 규약이 존재
무작정 pulbic 생성자를 제공하던 습관을 고치고 정적 팩토리 메서드의 장점을 고려하여 사용할 수 있도록 하자
'책 > Effective Java' 카테고리의 다른 글
다 쓴 객체 참조 해제하기 (0) | 2022.01.10 |
---|---|
불필요한 객체 생성 피하는 방법 (0) | 2022.01.09 |
다중 instance 방지 & 의존성 주입[Dependency Injection] (0) | 2022.01.04 |
Singleton 보장 방법 (0) | 2022.01.03 |
Builder 패턴 (0) | 2022.01.03 |