[객체 생성 패턴] Chapter 1-4. Singleton Pattern : 안전하고 단순한 싱글톤

 

✍️ 안전하고 단순한 싱글톤, 열거형

멀티 쓰레드에서 안전하게 싱글톤 패턴을 구현하는 여러 방법들이 존재하고 inner static class 생성 방식은 권장하는 방법 중 하나이다. 하지만 리플렉션에 대응할 수 없다는 단점을 가지고 있다.

 

만약 리플렉션까지 대응하고 싶다면 열거형을 사용해 싱글톤을 구현할 수 있다.

놀랍게도 열거형을 사용하면 아래 코드가 싱글톤의 전부다.

public enum Settings{
    INSTANCE;
}

 

열거형은 얼마든지 멤버 변수와 메서드도 정의할 수 있다.

public enum Settings{
    INSTANCE;

    private int value = 3;

    public int getValue() {
        return value;
    }
}
public class App {
    public static void main(String[] args) throws Exception {
        Settings settings = Settings.INSTANCE;

        System.out.println(settings.getValue());
    }
}

// 3

 

🍊 리플렉션과 열거형

열거형은 리플렉션을 통해 newInstance를 하지 못하도록 막아놨다.

public class App {
    public static void main(String[] args) throws Exception {
        Settings settings1 = Settings.INSTANCE;
        Settings settings2 = null;
        
        Constructor<?>[] declaredConstructors = Settings.class.getDeclaredConstructors();
        for(Constructor<?> constructor : declaredConstructors){
            constructor.setAccessible(true);
            settings2 = (Settings) constructor.newInstance("INSTANCE");
        }
    }
}
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects ...

 

나아가 열거형은 직렬화 & 역직렬화에도 안전하다. 놀랍게도 열거형은 serializable을 구현하고 있다. 눈에 보이진 않지만 열거형은 Enum 클래스를 상속받고 있으며 동시에 Enum 클래스는 serializable을 구현하고 있다. 

public class App {
    public static void main(String[] args) throws Exception {
        Settings settings1 = Settings.INSTANCE;

        try (ObjectOutput output = new ObjectOutputStream(new FileOutputStream("settings.obj"))) {
            output.writeObject(settings1);
        }

        Settings settings2 = null;

        try (ObjectInput input = new ObjectInputStream(new FileInputStream("settings.obj"))) {
            settings2 = (Settings) input.readObject();
        }

        System.out.println(settings1 == settings2);
    }
}

// true

 

⚠️ 열거형을 이용한 싱글톤은 항상 좋을까?

싱글톤을 구현할 때 권장하는 방법으로 static inner class열거형이 있다.

 

static inner class는 간단하면서도 동기화 키워드 없이 멀티 쓰레드 환경에서도 안전하지만, 리플렉션과 직렬화 & 역직렬화에 취약하단 단점이 있다.

 

반면, 열거형은 간단하면서도 기본적으로 리플렉션과 직렬화에 대응 가능하다. 그렇다면 열거형을 이용한 싱글톤 구현의 단점이 없는 걸까? 그렇지 않다. 첫째로 열거형도 이른 초기화의 성격을 갖고있다. 둘째로 열거형은 상속을 이용할 수 없다. 열거형은 오로지 Enum만을 상속받을 수 있다.

 

만약 구현하고자 하는 싱글톤 클래스가 특정 클래스를 반드시 상속받아야 한다면 열거형보단 static inner class를 사용하는 것을 고려해야 한다.