오직 한 인스턴스만 만드는 클래스를 싱글톤이라 부른다. 보통 함수같은 Stateless 객체 또는 본질적으로 유일한 시스템 컴포넌트를 그렇게 만든다.
싱글톤을 사용하는 클라이언트 코드를 테스트 하는게 어렵다.
싱글톤이 인터페이스를 구현한 게 아니라면 mock으로 교체하는게 어렵기 때문
싱글톤으로 만드는 두가지 방법이 있는데, 두 방법 모두 생성자를 private 으로 만들고 public static 멤버를 사용해 유일한 인스턴스를 제공한다.
Final 필드
public class Singleton1 {
private static final Singleton1 instance = new Singleton1();
public static Singleton1 getInstance() {
return instance;
}
private SingleTon1() {}
}
public class SingleTest {
public static void main(String[] args) {
Sigleton1 sigleton1 = Singleton1.getInstance();
}
}
리플렉션을 사용해서 private 생성자를 호출하는 방법을 제외하면 생성자는 오직 최초 한번만 만들도록 할 수 있다.
static 팩토리 메소드를 사용하는 방법에 비해 더 명확하고 간단하다.
static 팩토리 메소드
public class Singleton1 { private static final Singleton1 instance = new Singleton1(); public static Singleton1 getInstance() { return instance; } private SingleTon1() {}}public class SingleTest { public static void main(String[] args) { Sigleton1 sigleton1 = Singleton1.getInstance(); }}
API를 변경하지 않고로 싱글톤으로 쓸지 안쓸지 변경할 수 있다. 처음엔 싱글톤으로 쓰다가 나중엔 쓰레드당 새 인스턴스를 만든다는 등 클라이언트 코드를 고치지 않고도 변경할 수 있다.
직렬화
역직렬화 할때 readResolve() 호출이 이루어지는데 그 안에 싱글톤 Instance를 리턴하게 해주면 동일한 인스턴스를 리턴하게 되므로 안전하다.
위의 두 방법 모두 직렬화에 사용한다면 역직렬화 할 때 같은 타입의 인스턴스가 여러개 생길 수 있다. 그 문제를 해결하려면 인스턴스 필드에 transient를 추가(직렬화 하지 않겠다는 뜻)하고 readResolve 메소드를 다음과 같이 구현하면 된다.
private Object readResolve(){ // 진짜 객체를 반환하고 가짜 객체는 가비지 컬렉터에 맡긴다. return INSTANCE;}
Enum
직렬화/역직렬화 할 때 코딩으로 문제를 해결할 필요도 없고 리플렉션으로 호출되는 문제도 고민할 필요가 없는 방법이다.
public enum SingleTon3 { INSTANCE; public String getName() { return "형욱"; }}public class SingleTest { public static void main(String[] args) { String name = SingleTon3.INSTANCE.getName(); }}
Enum말고 상위 클래스를 상속해야 한다면 사용할 수 없다.(인터페이스는 가능하다)
현실적인 방법
public class UserRepository {}
public class Singleton1 {
public static final Sigleton1 instance = new Sigleton1();
private SingleTon1() {}
}
public class SingleTest {
public static void main(String[] args) {
Sigleton1 sigleton1 = new Sigleton1(); // 안됨
Singleton1 singleton2 = Singleton1.instance; // 재사용 가능
}
}