[이펙티브 자바] 아이템 3. private 생성자나 열거 타입으로 싱글턴임을 보증하라
effective-java에서 스터디를 진행하고 있습니다.
싱글턴
싱글턴의 개념
싱글턴이란 인스턴스를 오직 하나만 생성할 수 있는 클래스를 말한다. 싱글턴의 전형적인 예시로는 무상태 객체나 유일한 시스템 컴포넌트를 들 수 있다. 하지만 싱글턴 클래스는 타입을 인터페이스로 정의하고 그것의 구현체로 정의한 것이 아니라면 테스트하기 어렵다는 문제가 있다.
싱글턴을 만드는 방법
public static 멤버가 final 필드인 방식
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() {
}
public void speak() {
System.out.println("elvis");
}
}
private 생성자는 Elvis 인스턴스를 초기화할 때 딱 한 번만 호출되며, 전체 시스템에서 유일한 인스턴스임을 보장한다. 단, AccessibleObject.setAccessible()
를 사용하여 private 생성자를 호출할 수 있는데, 이러한 리플렉션으로 변조하는 방법은 2번째 객체가 생성될 때 예외를 던져서 막을 수 있다.
- 장점
- 해당 클래스가 싱글턴임이 API에 명백히 드러난다.
- 간결하다.
정적 팩터리 메서드를 public static로 제공하는 방식
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() {
}
public static Elvis getInstance() {
return INSTANCE;
}
public void speak() {
System.out.println("elvis");
}
}
리플렉션을 통한 변조 외에는 해당 방법도 전체 시스템에서 유일한 인스턴스임을 보장한다. 단지 필드를 private으로 바꾸고, 객체 반환을 정적 팩터리 메서드를 사용해 주고 있다.
- 장점
- API를 바꾸지 않고도 싱글턴이 아니게 변경할 수 있다.
- 가령, 정적 팩터리 메서드가 호출하는 스레드 별로 다른 인스턴스를 넘겨주도록 할 수 있다.
- 원한다면 제네릭 싱글턴 팩터리 메서드로 변경할 수 있다.
- 정적 팩터리의 메서드 참조를 공급자로 사용할 수 있다.
- 가령,
Elvis::getInstance
대신에Supplier<Elvis>
로 사용할 수 있다.
- 가령,
- API를 바꾸지 않고도 싱글턴이 아니게 변경할 수 있다.
위의 장점을 활용할 일이 없다면, 첫 번째 방식을 사용하는 편이 좋다.
열거 타입을 사용하는 방식
public enum Elvis {
INSTANCE;
public void speak() {
System.out.println("elvis");
}
}
가장 바람직한 방식은 열거 타입을 사용하는 것이다. 위 두 방식에 비해 리플렉션 공격에도 안전하고 코드도 깔끔하다. 또한, 아래에도 후술하겠지만 위 두 방식은 직렬화할 때 추가 코드를 넣어주어야 한다는 단점이 있다.
단, 생성하려는 싱글턴이 인터페이스를 상속 받는 것은 가능하지만 클래스를 상속 받을 수 없다는 것은 주의해야 한다.
싱글턴 클래스를 직렬화할 때 주의할 점
위 방식 중 첫 번째 또는 두 번째로 만든 싱글턴 클래스를 직렬화하려면 단순히 Serializable을 구현하는 것 외에, 모든 인스턴스 필드를 transient로 선언하고 readResolve()
메서드를 재정의하여 제공해야 한다.
private Object readResolve throws ObjectStreamException {
return INSTANCE;
}
출처
'스터디 > 이펙티브 자바 스터디' 카테고리의 다른 글
[이펙티브 자바] 아이템 6. 불필요한 객체 생성을 피하라 (0) | 2022.04.18 |
---|---|
[이펙티브 자바] 아이템 5. 자원을 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2022.04.18 |
[이펙티브 자바] 아이템 4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2022.04.18 |
[이펙티브 자바] 아이템 2. 생성자에 매개변수가 많다면 빌더를 고려하라 (2) | 2022.04.17 |
[이펙티브 자바] 아이템 1. 생성자 대신 정적 팩터리 메서드를 고려하라 (0) | 2022.04.17 |
댓글