[Effective Java] private 생성자나 enum타입으로 싱글톤을 보장하자

dongbin_Shin·2022년 1월 22일
0

이펙티브 자바

목록 보기
3/5
post-thumbnail

싱글톤이란?

싱글톤(Singletone)은 인스턴스를 오직 하나만 생성할 수 있는 클래스이다.

클래스를 싱글톤으로 만들면 이를 사용하는 클라이언트를 테스트하기 어려워질 수 있다. 타입을 인터페이스로 정의한 후, 그 인터페이스를 구현해 만든 싱글톤이 아니라면 가짜(mock) 구현으로 대체할 수 없기 때문이다.

여기서는 싱글톤을 보장하기 위한 방법으로 private생성자, enum을 활용한 방법을 알아본다.

1. public static final 필드 방식

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

위처럼 구현하면 private 생성자는 Single.INSTANCE를 초기화할 때 딱 한번만 호출된다.

public 혹은 protected 생성자가 없으므로 전체 시스템에서 인스턴스가 딱 하나뿐임을 보장할 수 있다.

이 방식의 장점은 다음과 같다.

  • API에 해당 클래스가 싱글톤임이 잘 드러남
  • 간결함

2. static factory 방식

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

Single.getInstance는 항상 같은 객체의 참조를 반환하므로 싱글톤이 보장된다.

이 방식의 장점은 다음과 같다.

  • API를 바꾸지 않아도 싱글톤이 아니게 변경 가능
  • 정적 팩토리를 제네릭 싱글톤 팩토리로 만들 수 있음
  • 정적 팩토리의 메서드 참조를 공급자(Supplier)로 사용 가능
    • Single::getInstanceSupplier<Single>로 사용

고려사항

private 생성자도 호출 가능하다.

두 방법 모두 예외사항이 있다.

권한이 있는 클라이언트가 리플렉션 API인 AccessibleObject.setAccessible을 이용해 private 생성자를 호출할 수 있다. 이를 방지하기 위해서는 생성자를 수정해 두번째 객체가 생성될 때 예외를 던지면 해결할 수 있다.

public class Single {
    public static final Single INSTANCE = new Single();
    private Single() {
        super();
        if(INSTANCE != null) {
            throw new DuplicatedSingleToneInstanceException(); //임의의 예외
        }
    }
}

직렬화를 주의하라.

또 직렬화를 할 때 고려해야 할 점이 있다.

위 두가지 방식으로 만든 싱글톤 클래스를 직렬화를 구현할 때 단순히 Serializable을 구현하는 것으로 그친다면 역직렬화할 때마다 새로운 인스턴스가 생성된다.

이를 방지하기 위해서는 모든 인스턴스 필드를 일시적(transient)라 선언하고 readResolve메소드를 제공해야 한다.

class Single implements Serializable {
    private static final transient Single INSTANCE = new Single();
    
    private Single() {...}
    private Single readResolve() {return INSTANCE;}

transient는 해당 필드 값이 직렬화되지 않게 하고, readResolve메소드는 역직렬화된 인스턴스를 무시하고 기존 인스턴스를 반환하도록 해 싱글톤을 유지할 수 있다.

Enum 타입 방식 (추천)

public enum Single {
    INSTANCE;
}

위의 예외사항들을 모두 방지하고 간결한 방식이다.

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

하지만 만들고자 하는 싱글톤이 enum 외의 클래스를 상속해야 하면 이 방식을 사용하지 못한다.

profile
멋있는 백엔드 개발자

0개의 댓글