JAVA/Effective Java
item 3. private 생성자나 열거 타입으로 싱글턴임을 보증하라.
Garonguri
2022. 4. 9. 18:21
728x90
* 싱글턴 이란?
- 인스턴스를 오직 하나만 생성할 수 있는 클래스.
- 그러나, 클래스를 싱글턴으로 만들면 이를 사용하는 클라이언트를 테스트 하기가 어렵다.
- 객체를 한 번만 생성하기 때문에 재사용이 가능하다.
- 메모리 낭비를 막을 수 있고, 전역성을 가져 다른 객체와 공유가 가능하다.
* 싱글턴을 만드는 방식 3가지
- 방법 1, 2를 통해 만든 싱글턴 클래스를 직렬화 하기 위해서는 모든 instance를 transient (직렬화 하지 않겠다는 의미) 선언하고, readResolve 메서드를 제공해야만 역 직렬화 시 새로운 인스턴스가 만들어짐을 방지할 수 있다. 그렇지 않으면, 초기화해둔 인스턴스가 아닌 다른 인스턴스가 반환된다.(약간 복잡)
- 직렬화(Serialization): 객체를 데이터 스트림으로 만드는 것. (객체에 저장된 데이터를 스트림에 쓰기 위해 연속적인 데이터로 변환하는 것)
- 역직렬화(Deserialization): 스트림으로부터 데이터를 읽어서 객체를 만드는 것
- 1. public static final field 방식
- private 생성자는 field를 초기화할 때 딱 한 번 호출. -> 전체 시스템에서 인스턴스가 하나임을 보장한다.
- 리플렉션을 사용해 private 생성자를 호출하는 방법을 제외하고는 오직 한번만 호출된다.
- 클라이언트는 생성자를 호출할 수 없다. (클라이언트가 권한이 있는 경우는 예외, 두 번째 객체가 생성될 때 예외처리 해서 방어 가능.)
- 해당 클래스가 싱글턴임이 API에 명백히 드러나며, 간결하다.
public class item3 {
//final이므로 절대 다른 객체를 참조할 수 없다.
public static final item3 INSTANCE = new item3();
private item3(){ ... }
public void getitem(){...}
}
- 2. 정적 팩터리 방식
- 스레드별로 다른 인스턴스를 넘겨주게 할 수 있기 때문에, API를 바꾸지 않고도 싱글턴이 아니게 변경할 수 있다.
- 정적 팩터리를 제네릭 싱글턴 팩터리로 만들 수 있다.
- 정적 팩터리의 메서드 참조를 공급자로 사용할 수 있다. (getInstance -> Supplier<class>)
public class item3 {
public static final item3 INSTANCE = new item3();
private item3(){ ... }
//항상 같은 객체의 참조를 반환하므로 제 2의 instance를 만들지 않는다.
public static item3 getInstance(){return INSTANCE;}
public void print(){ ... }
}
- 3. 열거 타입 방식 -> 바람직!
- public 방식보다 간결하고, 직렬화가 쉽다.
- 아주 복잡한 직렬화 상황, 리플렉션 공격에서도 제2의 인스턴스가 생기지 않는다.
public enum item3 {
INSTANCE;
public void print() { ...}
}
만들려는 싱글턴이 Enum class 외의 클래스를 상속하지만 않는다면 대부분 이 방법이 가장 좋은 방법이다.
-> enum을 사용한다면 직렬화, 역직렬화 문제 해결. 다른 상위 클래스를 상속할 수 없기 때문. (인터페이스는 구현 할 수 있음)
728x90