private 생성자나 열거 타입으로 싱글턴임을 보증하라

Gunjoo Ahn·2022년 7월 29일
0
post-thumbnail

Singleton 이란

인스턴스를 오직 하나만 생성할 수 있는 클래스이다.

Class 방식

singleton 방식에는 보통 두 가지가 있는데, 모두 private 생성자를 사용하여 유일한 인스턴스를 초기화한다.

  1. public static final 맴버 변수로 유일한 인스턴스를 접근
  2. 정적 팩터리 메서드를 통하여 유일한 인스턴스를 반환하여 접근

private 생성자이므로 클래스가 초기화 될 때 만들어진 인스턴스가 전체 시스템에서 하나뿐임이 보장된다. 리플렉션으로 private 생성자를 호출할 수 있는 데, 예외를 통하여 방어해야한다.

Ⅰ. 장점

1번의 장점은 싱글턴임이 명확하고, 간결하다는 점이다.

Ⅱ. 장점

2번의 장점은 API의 변경 없이 singleton이 아니게 수정할 수 있다는 점이다.
또 원한다면 정적 팩터리를 제네릭 싱글턴 팩터리로 만들 수 있다.
마지막으로 정적 팩터리의 메서드 참조를 supplier로 사용할 수 있다.

Supplier - This is a functional interface whose functional method is get(). There is no requirement that a new or distinct result be returned each time the supplier is invoked. Oracle

public interface Supplier<T> {
    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}
Supplier<String> supplier= ()-> "HelloWorld";
String result = supplier.get();
System.out.println(result);
// HelloWorld

CodeChaCha


Singleton을 직렬화 하려면?

Singleton을 직렬화하려면 단순히 Serializable을 구현한다고 선언하는 것만으로는 부족하다. 모든 인스턴스 필드를 일시적(transient)이라고 선언하고 readResolve 메서드를 제공해야 한다.

Transient - Variables may be marked transient to indicate that they are not part of the persistent state of an object. Oracle

Serialization is the process of converting an object into a byte stream, and deserialization is the opposite of it.
When we mark any variable as transient, then that variable is not serialized. Baeldung

이렇게 하지 않으면 직렬화된 인스턴스를 역직렬화할 때마다 새로운 인스턴스가 생성된다. Singleton이 아니게 된다는 이야기다. 가짜 singleton 인스턴스의 생성을 예방하고 싶다면

// Singleton임을 보장해주는 readResolve 메서드
private Object readResolve() {
	// 진짜 singleton 인스턴스를 반환하고, 가짜 singleton 인스턴스는 gc에 맡긴다
    return INSTANCE;
}

👍Enum 방식

Singleton을 만드는 방법이 하나 더 있는데, 원소가 하나인 열거 타입을 선언하는 것이다.

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

1번인 public 필드 방식과 유사하지만, 더 간결하고, 추가 노력 없이 직렬화할 수 있고, 심지어 아주 복잡한 직렬화 상황이나 리플렉션 공격에도 막아준다.

💡대부분의 상황에서는 원소가 하나뿐인 열거 타입이 싱글턴을 만드는 가장 좋은 방법이다.

싱글턴이 Enum외의 클래스를 상속해야 한다면 사용할 수 없는 방법이다.
다만 Enum 클래스는 interface를 implement할 수는 있다.

profile
Backend Developer

0개의 댓글