(Effective Java 2/E) 103. Item 3 - Enforce the singleton property with a private constructor or an enum type

Enforce the singleton property with a private constructor or an enum type

  • 2판 제목 : private 생성자나 enum 자료형은 싱글턴 패턴을 따르도록 설계하라
  • 3판 제목 : private 생성자나 열거 타입으로 싱글턴임을 보증하라.

싱글턴(singleton) 은 오직 하나의 객체만 만들 수 있는 패턴이다.

시스템 컴포넌트 등을 구현할 때 매우 유용하지만, 싱글턴 클래스는 테스트가 어려워진다는 단점도 가지고 있다.

참고 (Working Effectively with Legacy Code) 009. I Can’t Get This Class into a Test Harness
싱글턴 클래스의 테스트가 어려운 이유는 이 포스팅의 4번 항목인 “까다로운 전역 의존 관계”를 참고하자.

먼저 싱글턴을 만드는 방법들에 대해 알아보자.

1. public static final 변수로 구현

정적 멤버변수를 final로 선언하여 싱글턴으로 만들 수 있다.

1
2
3
4
5
public class Sigleton {
public static final Singleton INSTANCE = new Singleton();

private Singleton() {}
}

Singleton 클랫으ㅢ 생성자는 Singleton.INSTANCE를 초기화하는 시점에 단 한 번 호출된다.

단 자바의 리플렉션을 이용하면 private 생성자도 호출이 가능하므로 취약점이 존재하므로, 별도의 방어 로직을 추가해야한다.

2. 정적 팩토리 메서드로 구현

정적 팩토리 메서드로 싱글턴을 구현할 수도 있다.

1
2
3
4
5
6
7
8
9
public class Singleton {
private static final Singleton INSTANCE = new Singleton();

private Singleton() {}

public static Singleton getInstance() {
return INSTANCE;
}
}

Singleton.getInstance 함수는 항상 같은 객체에 대한 참조를 반환한다.

다만 JVM에서는 정적 팩토리 메서드를 인라인으로 처리해버리면 성능의 저하가 발생한다.

리플렉션에 대한 방어로직도 여전히 필요하며, 직렬화가 필요할 경우 별도의 구현을 추가해야한다.

3. enum으로 구현

열거형 타입으로도 싱글턴을 구현할 수 있다.

1
2
3
public enum Singleton {
INSTANCE;
}

열거형 타입으로 작성하면 코드가 상대적으로 간결하고, 직렬화 또한 별도 구현없이 수행할 수 있다는 장점이 있다.

추가적으로 리플렉션에 대한 별도의 방어로직도 필요하지않다.

어쩌면 열거형 타입으로 생성하는 싱글턴이 가장 좋은 방법이라고 볼 수 있다.

하지만 만들려는 싱글턴이 Enum 이외의 클래스를 상속하게 되면 사용할 수 없는 방법이다.