[Effective Java] ch1 - item1. 생성자 대신 정적 팩터리 메서드를 고려하라
ch1.객체의 생성과 파괴
item1. 생성자 대신 정적 팩터리 메서드를 고려하라
정적 팩터리 메서드의 장점
- 이름을 가질 수 있다.
- 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.
- 반환 타입의 하위 타입의 객체를 반환 할 수 있다.
- 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환 할 수 있다.
- 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
이름을 가질 수 있다.
- 이름을 다르게 해도 메소드 시그니처가 완벽히 같아서 생성자 2개를 만드는 것이 불가능 하지만 정적 팩터리 메서드를 사용하면 가능합니다.
- 이름을 가질 수 있다는 건 메소드 매개변수의 시그니처가 동일해도 메소드를 정의할 수 있습니다.
public class Order { private boolean prime; private boolean urgent; private Product product; public Order(Product product, boolean prime) { this.product = product; this.prime = prime; } public Order(Product product, boolean urgent) { this.product = product; this.urgent = urgent; } }
```java public class Order {
private boolean prime; private boolean urgent; private Product product;
public static Order primeOrder(Product product){ Order order = new Order(); order.prime = true; order.product = product; return order; }
public static Order urgentOrder(Product product){ Order order = new Order(); order.urgent = true; order.product = product; return order; }
}
-----
### 인스턴스 통제 클래스를 만들 수 있습니다.
- 반복되는 요청에도 같은 객체를 반환해서 이를 **인스턴스 통제 클래스**라 합니다.
- 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.
* **Boolean 클래스의 valueOf** 메소드입니다.
* 매개변수에 따라 미리 생성된 인스턴스를 반환합니다.
- **생성 비용이 큰 객체가 자주 요청**되는 상황에선 성능 향상을 노릴 수 있습니다.
* 객체를 캐싱, 재사용 하는 패턴인 **flyweight pattern**의 근간이 됩니다.
- non instantiable class(인스턴스화 불가능 클래스) 를 만들 수 있습니다.
- 대표적인 non instantiable class로는 유틸리티성 클래스가 있습니다. 보통 class에 `final`을 붙여 만듭니다.
```java
//미리 생성된 인스턴스
public static final Boolean TRUE = new Boolean(true);
//정적 팩토리 메소드
@IntrinsicCandidate
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
반환 타입의 하위 타입 객체를 반환할 수 있다.
- 구현 클래스 공개하지 않고도 그 객체를 반환할 수 있습니다.
- java 8 이후 아래처럼 인터페이스에 정적 팩토리 메서드 선언이 가능해져서 인터페이스의 인스턴스를 대신 생성 해주는 동반 클래스(companion class)가 없어도 됩니다.
- java 9 이후엔 아래처럼 private이 가능해졌습니다.
public interface HelloService { String hello(); static private void prepare(){ // blah blah } static HelloService of(String lang){ if(lang.equals("kr")){ return new HelloKorean(); }else{ return new HelloAmerican(); } } }
정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
- 인터페이스만 있고 인터페이스 구현체는 존재하지 않아도 된다. (정적 팩토리 메소드가 있는 상태에서..)
- 아래와 같이 서비스 프로바이더 프레임워크를 이용해 호출 할 수 있습니다.
public interface HelloService { String hello(); }
```java public class HelloServiceFactory {
public static void main(String[] args) { ServiceLoader
loader = ServiceLoader.load(HelloService.class); Optional helloServiceOptional = loader.findFirst(); helloServiceOptional.ifPresent(helloService -> { System.out.println(helloService.hello()); }); }
}
#### 🤔 왜 서비스 프로바이더 프레임워크를 사용할까?
- 아래처럼 **HelloChina(구체적인 구현체)** 를 직접 호출하면 되지 굳이 왜 **ServiceLoader**와 같은 서비스 프로바이더 프레임워크니 하는걸까?
* ServiceLoader를 사용하면 구체적인 구현체 의존적이지 않습니다.
* 어떤 HelloService 구현체가 올지 모르기 때문에 굉장히 유연한 코드를 짤 수 있습니다.
- **JDBC** 가 대표적인 서비스 프로바이더 프레임워크입니다!
```java
public class HelloServiceFactory {
public static void main(String[] args) {
HelloService helloService = new HelloChina();
System.out.println(helloService.hello());
}
}
정적 팩토리 메소드의 단점
- 상속을 할 수 없다.
- 프로그래머가 찾기 어렵다.
상속을 할 수 없다.
- 상속보다 컴포지션 사용을 하게 되므로 다르게 보면 장점이다.
프로그래머가 찾기 어렵다.
- 메소드이기 때문에 생성자와 다르게 눈에 잘 띄지 않음.
- 정적 팩터리 메서드에서 흔히 사용하는 명명 방식들을 사용하면 찾기 쉬워지는 편이다.
- from : 매개변수 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메서드
Date date = Date.from(instance)
- of : 매개변수 다수 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드
Set<Rank> faceCards = EnumSet.of(Jack, Queen, King)
- valueOf : from 과 of 보다 조금 더 자세한 버전입니다.
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE)
- instance (getInstance) : 매개변수로 명시한 인스턴스를 반환하지만 같은 인스턴스임을 보장하지 않습니다.
StackWalker luke = StackWalker.getInstance(instance)
- create (newInstance) : 매번 새로운 인스턴스를 생성합니다.
Object newArray = Array.newInstance(classObject, arrayLen)
- get(Type) : instance와 같지만 반환 받길 원하는 클래스와 정적 팩터리 메소드가 정의된 클래스가 다릅니다.
FileStore fileStore = File.getFileStore(instance)
- new(Type) : create와 같지만 반환 받길 원하는 클래스와 정적 팩터리 메소드가 정의된 클래스가 다릅니다.
BufferdRedader br = Files.newBufferdReader(instance)
- type : get(Type), new(Type)의 간단한 명명법
List<Object> list = Collections.list(objectList)
- from : 매개변수 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메서드
댓글남기기