아이템 3. private 생성자나 enum자료형은 싱글턴 패턴을 따르도록 설계하라.

03. private 생성자나 enum자료형은 싱글턴 패턴을 따르도록 설계하라.

싱글턴 패턴(singleton-pattern): 시스템에서 단일 객체만 존재 하도록 구성하는 패턴으로 보통 유일할 수밖에 없는 시스템 컴포넌트를 타나낸다.

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

public final Elvis INSTANCE = new Elvis();처럼 바로 INSTANCE에 접근 하는 벙법이 있으나, 이는 정적 팩토리 메서드(getInstance)를 사용하는 방법보다 탄력적이지 않다.

정적 팩토리 메서드를 사용하면 API를 변경하지 않고도 상황에 따라 전략적으로 싱글턴 패턴을 포기할 수 있다.

아래와 같이, private Elvis(){}로 정의해도 리플렉션(reflection)기능을 통해 private 생성자를 호출할 수 있다.

import java.lang.reflect.Constructor;

public class PrivateInvoker{
	public static void main(String[] args) throws Exception{
		Constructor<?> con = Elvis.class.getDeclaredConstructors()[0];
		con.setAccessible(true); // set
		Elvis e = (Elvis)con.newInstance();		
	}
}

싱글턴 클래스를 직렬화 가능(Serializable)클래스로 만들려면 클래스 선언에 implements Serializable을 추가하는 것으로는 부족하다. 싱글턴 특성을 유지하려면 모든 필드를 transient로 선언하고 readResolve메서드를 추가해야 한다. 그렇지 않으면 serialize된 객체가 역직렬화(deserialize)될 때마다 새로운 객체가 생성된다.

	private Object readResolve(){
		// 동일한 Elvis 객체가 반환되도록 하는 동시에, 가짜 Elvis객체는 쓰레기 수집기가 처리하도록 만든다.
		return INSTANCE;
	}

JDK 1.5 부터는 enum을 활용한 싱글턴을 지원한다.

public enum Elvis{
	INSTANCE;
	public void leaveTheBuilding() { ... }
}

위 접근법은 기능적으로 public 필드를 사용하는 구현법과 동일하다. 이와 같은 간략한 방법은 직렬화 처리도 JVM이 자동으로 처리해 준다. 또한 리플렉션을 사용한 private 생성자 접근에도 안전하다.

아직 널리 사용되는 접근법은 아니지만, 원소가 하나뿐인 enum자료형이야 말로 싱글턴을 구현하는 가장 좋은 방법이다.