아이템 3. private 생성자나 열거 타입으로 싱글턴임을 보증하라 - KwangtaekJung/book-effective-java GitHub Wiki
아이템 3. private 생성자나 열거 타입으로 싱글턴임을 보증하라.
싱글턴(singleton) 이란
- 인스턴스를 오직 하나만 생성할 수 있는 클래스
- 전형적인 싱글턴의 예로는 Item24에 나오는 함수와 같은 무상태(stateles) 객체나 설계 상에 유일 해야하는 시스템 컴포넌트
싱글턴을 만드는 방식
- 첫번재, public static final 필드 방식의 싱글턴
- 리플렉션을 통해 생성자를 호출할 수는 있지만 생성자에서 예외를 던지도록 만들어서 방어할 수 있다.
- 싱글턴임을 명확하게 파악할 수 있고 비교적 코드가 간결해 지는 장점
package book.effective.java.ch02.item03;
public class ElvisWithPublicStaticFinal {
public static final ElvisWithPublicStaticFinal INSTANCE = new ElvisWithPublicStaticFinal();
private ElvisWithPublicStaticFinal() {
//...
}
public void leaveTheBuilding() {
//...
}
@Test
@Order(10)
@DisplayName("public static final 필드 방식의 싱글턴")
public void singleton_PublicStaticFinal() {
ElvisWithPublicStaticFinal elvisWithPublicStaticFinal1 = ElvisWithPublicStaticFinal.INSTANCE;
ElvisWithPublicStaticFinal elvisWithPublicStaticFinal2 = ElvisWithPublicStaticFinal.INSTANCE;
System.out.println("elvisWithPublicStaticFinal1 = " + elvisWithPublicStaticFinal1);
System.out.println("elvisWithPublicStaticFinal2 = " + elvisWithPublicStaticFinal2);
assertThat(elvisWithPublicStaticFinal1).isEqualTo(elvisWithPublicStaticFinal2);
}
@Test
@Order(11)
@DisplayName("public static final 필드 방식의 싱글턴 - 예외: reflection")
public void singleton_PublicStaticFinal_reflaction() throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
ElvisWithPublicStaticFinal singletonInstance = ElvisWithPublicStaticFinal.INSTANCE;
System.out.println("singletonInstance = " + singletonInstance);
ElvisWithPublicStaticFinal singletonReflection;
Constructor<ElvisWithPublicStaticFinal> constructor = ElvisWithPublicStaticFinal.class.getDeclaredConstructor();
constructor.setAccessible(true);
singletonReflection = constructor.newInstance();
System.out.println("singletonReflection = " + singletonReflection);
assertThat(singletonInstance).isNotEqualTo(singletonReflection);
}
- 두번재, Item1 정적 팩터리 방식의 싱글턴
- getInscance 메소드는 항상 같은 인스턴스를 반환하기 때문에 역시 인스턴스가 하나뿐임을 보증한다.
- 리플렉션을 통한 생성은 첫번째 방법과 마찬가지로 막을 수 있다.
- 이 방법은 싱글턴이 아닌 클래스로 변경할 때 이점을 가진다. 또 제네릭을 사용할 수 있다.
public class ElvisWithStaticFactory {
private static final ElvisWithStaticFactory INSTANCE = new ElvisWithStaticFactory();
private ElvisWithStaticFactory() {
//...
}
public static ElvisWithStaticFactory getInstance() {
return INSTANCE;
}
public void leaveTheBuilding() {
//...
}
}
-
위 두 방법의 단점은 직렬화를 위해 Serializable 을 구현할 때 발생한다.
- 직렬화된 인스턴스를 역직렬화할 때 마다 새로운 인스턴스가 만들어지기 때문이다.
- 이를 방어하기 위해서는 모든 인스턴스필드를 transient 선언하고 readResolve 메서드를 제공해야한다.
-
세번째, 열거 타입 방식의 싱글턴
- 대부분의 상황에서 원소가 하나뿐인 열거 타입이 싱글턴을 만드는 가장 좋은 방법이다.??
- 하지만 다른 클래스를 상속해야 한다면 이 방법은 사용할 수 없습니다.
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() {
//...
}
}
참고
- https://blog.riyenas.dev/59
- https://webdevtechblog.com/%EC%8B%B1%EA%B8%80%ED%84%B4-%ED%8C%A8%ED%84%B4-singleton-pattern-db75ed29c36
디자인 패턴 중 하나인 싱글턴 패턴(Singleton Pattern) 에 대해서 배워보겠습니다. 자바의 싱글턴과 스프링의 싱글턴이 어떻게 다른지, Thread-safe 한 싱글턴 구현방법 등에 대해서 배워보겠습니다.