싱글톤(Singleton)

•  인스턴스를 오직 한 개만 생성할 수 있는 클래스를 싱글톤 이라고 한다.

•  객체 생성 요청이 여러번 발생하더라도 새롭게 메모리를 할당하여 인스턴스를 만드는 것이 아닌,
    기존에 생성되어 있던 인스턴스를 참조한다.

•  사용 예시 : Scheduling 처리 객체 등

•  장점 : 불필요한 메모리 사용을 줄일 수 있다

•  단점 : 이를 사용하는 클라이언트를 테스트하기 어려워 질 수 있다.

    ◦  싱글톤 인스턴스를 가짜(mock) 구현으로 대체할 수 없기 때문.


싱글톤을 만드는 두가지 방법

•  공통점 : 생성자는 private으로, 인스턴스에 접근할 수 있는 유일한 수단 = public static 멤버

1) public static 멤버를 final 필드로 선언

public class Elvis {
		**public** static **final** Elvis INSTANCE = new Elvis();
		private Elvis() { ... }
}

•  private Elvis 생성자는 Elvis.INSTANCE 를 생성할 때(new Elvis) 딱 한 번 호출됨

•  다른 클래스에서 참조할 수 없는 생성자(private) 이므로 Elvis 클래스가 가지는 인스턴스는 Elvis.INSTANCE 단 한 개 뿐임을 보장할 수 있다.

•  예외) 권한이 있는 클라이언트가 AccessibleObject.setAccessible(리플렉션 API)을 사용해 private 생성자를 호출하는 경우

   ◦  리플렉션 : 개발자가 클래스의 구조를 확인하거나, 값/메소드를 호출해야 할 때 사용됨 (런타임 동작 검사 및 수정에 주로 이용된다)

   ◦  setAccessible : 필드나 메서드의 접근제어 지시자에 의한 제어를 변경하는 메서드

Elvis.setAccessible(true); // 이제 외부에서 private Elvis() 접근가능!

•  장점) 해당 클래스가 싱글톤임이 명백하게 드러나며 간결하다.

   ◦  public static 필드가 final 이므로 절대 다른 객체를 받을 수 없기 때문

 

2) 정적 팩토리 메서드를 public static 멤버로 제공

public class Elvis {
		**private** static **final** Elvis INSTANCE = new Elvis();
		private Elvis() { ... }
		**public static Elvis getInstance() { return INSTANCE; }**
}

•  Elvis.getInstance : 항상 같은 객체의 참조를 반환하므로 제2의 Elvis 인스턴스는 생성될 수 없음.

•  장점 1) API를 바꾸지 않고도 싱글톤이 아니게 변경 가능 (스레드별로 다른 인스턴스를 넘겨주는 등)

•  장점 2) 정적 팩토리 → 제네릭 싱글톤 팩토리 로 변경 가능

•  장점 3) 정적 팩토리의 메서드 참조를 공급자(supplier)로 사용 가능

   ◦  Elvis::getInstance 를 Supplier<Elvis>로 사용하는 식


싱글톤 클래스의 직렬화

•  직렬화란? : 자바 시스템 내부에서 사용되는 객체 또는 데이터들을 외부의 자바 시스템에서도 사용할 수 있도록
                       바이트(byte) 형태로 데이터 변환하는 기술

•  역직렬화 : 바이트로 변환된 데이터를 다시 객체로 변환하는 기술 (JVM)

•  모든 객체 필드를 일시적(transient)이라고 선언하고 readResolve 메서드를 제공해야 함

   ◦  안그러면 직렬화된 인스턴스를 역직렬화할 때 마다 새로운 인스턴스가 만들어진다.

       -  예 ) 새로운 인스턴스를 생성하는 것 = 가짜 Elvis를 생성하는 것

   ◦  역직렬화 과정에서 만들어진 인스턴스 대신 기존에 생성된 싱글톤 인스턴스를 반환해주는 역할

public class Elvis implements Serializable {
	private static final Elvis INSTANCE = new Elvis();
	private Elvis() { ...	}
	public static Elvis getInstance() { return INSTANCE; }

	**private Object readResolve() { return INSTANCE;	}**
}

싱글톤을 만드는 세번째 방법 : 원소가 한개인 Enum 클래스 사용하기

public enum Elvis { INSTANCE; }

•  코드가 매우 간결하고! 또 더 쉽게 직렬화가 가능하다.

•  복잡한 직렬화 상황 혹은 Reflection에 의해 제 2의 인스턴스가 생기는 일을 방지한다.

   ◦  enum은 기본적으로 serializable 하기 때문에 serializable interface 를 따로 구현할 필요가 없다. 따라서 역직렬화 시에 runtime 내부에 존재하는 enum class, 즉 동일한 값을 참조한다.

   ◦  enum class는 외부에서 액세스 할 수 있는 생성자 자체가 없으므로 reflection에 면역! (enum class 생성자 = Sole Constructor / 컴파일러에서 사용하는 것으로 사용자가 직접 호출 불가)

   ◦  따라서 대부분의 상황에서 Enum 선언이 싱글톤을 만드는 가장 좋은 방법이다.

       -  단, 만들고자 하는 싱글톤이 Enum 외의 클래스를 상속해야 한다면 이 방법은 사용할 수 없다.


참고자료

 

싱글톤(Singleton) 패턴이란?

이번 글에서는 디자인 패턴의 종류 중 하나인 싱글톤 패턴에 대해 알아보자. 싱글톤 패턴이 무엇인지, 패턴 구현 시 주의할 점은 무엇인지에 대해 알아보는 것만으로도 많은 도움이 될 것이라

tecoble.techcourse.co.kr

 

[Java] Enum의 사용법

안녕하세요. 지난 시간엔 EnumClass가 무엇이고 어떤 장점들이 있는지 알아보았습니다. 2017/06/27 - [Java] - [Java] enum 이란? (enum 개념익히기) 이번 시간은 Enum 사용방법을 더 알아보면서 Enum에 대한 이.

limkydev.tistory.com

 

[Java & Kotlin] enum class가 완벽한 싱글톤이라 불리는 이유

싱글톤 패턴(Singleton Pattern)이란? 싱글톤은 애플리케이션 상 특정 클래스가 최초 한 번만 메모리를 할당하고 그 메모리에 인스턴스를 만들어 사용하는 디자인 패턴을 의미한다. 객체 생성 요청이

dataportal.kr

 

[java] enum 의 serialize 에 대한 이야기

- enum 은 기본적으로 serializable 하다. - enum 의 serialization 은 다른 serialization 과 조금 다르다. enum 의 serialize 결과는 constant 의 이름뿐이다. enum serialize 시 ObjectOutputStream 에서는 'n..

aroundck.tistory.com