싱글턴(singleton)이란 인스턴스를 오직 하나만 생성할 수 있는 클래스이다.
싱글턴을 만드는 방식은 둘 중 하나다. 두 방식 모두 private으로 감추고, 인스턴스에 접근할 수 있는 수단으로 public static 멤버를 하나 만든다.
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() {...}
public void leaveTheBuilding() {...}
}
생성자는 INSTANCE 를 초기화할 때 한 번만 호출된다. 그렇기에 전체 시스템에서 해당 클래스는 하나만 생성됨을 보장할 수 있게된다.(단 향후 배울 리플랙션에서 private 생성자를 호출하는 법을 알게된다.)
해당 코드의 장점은 해당 클래스가 싱글턴임이 명백히 보이며, 코드가 되게 간결해진다는 것이다.
public class Elvis {
priavte static final Elvis INSTANCE = new Elvis();
private Elvis() {...}
public static Elvis getInstance( return INSTANCE; }
public void leaveTheBuilding() {...}
}
Elvis.getInstance 는 항상 같은 객체에 대한 참조를 반환하므로 해당 클래스가 하나만 생성된다.(이 또한 리플렉션에 뚫린다.)
이 코드의 장점은 바꾸지 않고도 싱글턴이 아니게 변경할 수 있으며 정적 팩터리를 제네릭 싱글 팩토리 패턴으로 바꿀 수 있다는 점이다.
또한 정적 팩토리 메서드 참조를 supplier로 사용할 수 있다는 점이다.(Supplier<Elvis>)
해당 방식으로 싱글턴 클래스를 직렬화하려면, Serializable 로는 부족하다.
모든 인스턴스 필드를 일시적(transient)로 선언하고 readResolve 메서드를 제공해야 한다.
만약 이렇게 하지 않으면 역직렬화할 시 새로운 인스턴스가 만들어진다.
사실 싱글턴을 만드는 추가적인 꿀 방법이 있는데, 이는 원소가 하나인 enum을 선언하는 것이다.
public enum Elvis {
INSTANCE
public void leaveTheBuilding() {...}
}
이 방법은 굉장히 깔끔하고 간결하며, 추가 노력 없이도 직렬화가 가능하며, 리플렉션에서도 인스턴스가 한 개 더 생성되는 것을 막아준다.
(단 만들려는 싱글턴이 Enum 이외의 클래스에 대한 상속이 필요할 시 사용불가(인터페이스를 구현하도록 선언할 수는 있다.))
만약 위와 같은 장점이 필요하지 않으면 public 필드 방식이 좋다.(사실 자바에서는 내부적으로 생성자를 기본적으로 제공하고 있다.)
결론
상속을 받을 필요가 없을 경우 Enum 싱글턴, 그 외 방법은 직렬화와 리플랙션에 대한 방어가 필요하다.
'독후감 > Effective Java' 카테고리의 다른 글
6. 불필요한 객체 생성을 피하라 (0) | 2021.03.31 |
---|---|
5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2021.03.30 |
4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2021.03.18 |
2. 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2021.03.18 |
1. 생성자 대신 static factory method를 사용해보자 (0) | 2019.02.18 |