[Effective Java] 아이템3 - private 생성자나 열거 타입(Enum)으로 싱글턴임을 보증하라

이 글은 Effective Java 책을 읽으면서 중요한 내용 및 알아야 하는 지식들을 정리한 글입니다. 또한, 백기선님의 Youtube 강의영상도 참고 했습니다. https://www.youtube.com/watch?v=OwkXMxCqWHM&list=PLfI752FpVCS8e5ACdi5dpwLdlVkn0QgJJ&index=2

싱글턴(singleton)이란 인스턴스를 오직 하나만 생성할 수 있는 클래스를 말한다. 싱글턴을 만드는 방식은 두 가지가 있다. 두 방식 모두 생성자는 private으로 감춰두고, 인스턴스에 접근할 수 있는 수단으로 1) public static final 멤버 변수로 지정한다. 또는 2) 정적 팩터리 메서드를 public static 멤버로 제공한다.

final 필드

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

private 생성자는 최초 딱 한번 호출 되기 때문에 Elvis는 싱글턴이 된다.

예외적으로 리플렉션 API [아이템65]인 AcceesibleObject.setAccessible 를 사용해 private 생성자를 호출할 수 있다.

정적 팩터리 메서드

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

getInstance()를 통해서만 instance를 얻을 수 있다. 이러한 방식은 추후에 싱글턴이 아니라 다른 인스턴스를 반환하도록 바꾸고자 할 때 클라이언트의 코드를 수정하지 않아도 되는 것이 장점이다.

public static Elvis getInstance() {
  return new Elvis();  // 이렇게 getInstance() 안에 내용만 바꿔주면 된다.
}

정적 페터리의 메서드 참조를 공급자(supplier)로 사용할 수 있다.

Supplier<Singleton1> singletonSupplier = Singleton1::getInstance;

직렬화

위 두가지 방법 모두 직렬화를 사용한다면 역직렬할 때마다 새로운 인스턴스가 만들어진다.

백기선님의 유튜브 내용에 따르면, Java에서 역직렬화를 하면, 항상 readResolve 메소드가 호출이 되므로 객체가 N개 생성될 수 있다.

이러한 문제점을 방지하기 위해서 필드를 transient로 선언하고 아래와 같은 readResolve 메서드를 제공해야 한다. instance를 transient로 만들면 자바에서 역직렬화 하지 않는다.

// 싱글턴임을 보장해주는 readResolve 메서드 private
Object readResolve() {
  return INSTANCE;
}

Serializable 는 회사 소스코드에서도 많이 봤는데 한번도 이게 뭐지라는 생각을 한적이 없다. 그래서 따로 공부했다. 자세한 포스팅은 직렬화 에서 확인하자.

Enum

이 책에서 제일 권장하는 방법은 원소가 하나인 Enum 을 선언하는 것이다.

public enum Elvis {
    INSTANCE;
    public void leaveTheBuilding() {
        System.out.println("Sweet Home");
    }
}

정말 간결하고, 아무것도 해줄 필요도 없다. 좀 당활스러운 코드지만, 싱글턴임을 보장해야 할 경우 위와 같은 방법도 있다는 것을 알면 되겠다.

백기선님 말대로 현실적으로 비즈니스에서 위와 같이 구현하는 경우는 없을 것 같다. 왜냐하면 Spring이 다 해주니까..!

스프링에서 말하는 singleton은 applicationContext 안에서의 singleton 이다. - 커스텀한 @Scope를 만들 수 있단다.. [출처] 백기선님 유튜브 방송 내용중에서

댓글남기기