공부하는 스누피

[디자인 패턴] 자바의 싱글턴(Singleton) 본문

Languages/Java

[디자인 패턴] 자바의 싱글턴(Singleton)

커피맛스누피 2021. 2. 16. 23:26

싱글턴이란?

싱글턴(Singleton)은 디자인 패턴 중 생성 패턴으로, 해당 클래스에서 생성되는 인스턴스가 단 하나임을 보장한다.

클래스에서 인스턴스가 하나임이 보장되면 인스턴스를 쉽게 통제할 수 있으며, 재사용이 쉽다. 하지만 mock이 불가능해 클라이언트를 테스트하기 어려워진다는 단점이 있다. mock은 인스턴스를 여러 개 만들 수 있어야 가능하기 때문이다.

예를 들어, 무상태 객체(stateless object)나 시스템 컴포넌트에서 싱글턴 패턴을 사용한다. 

 

싱글턴 만드는 방식

싱글턴 만드는 방식은 크게 두 가지가 있으나 둘 다 생성자를 private으로 숨긴다.

private Contructor(){
	... 생성자를 숨긴다 ...
}

1) public static final 멤버 변수에 인스턴스를 저장하는 방식

public class Class{
	public static final Class INSTANCE = new Class();
    private Class(){
    	super();
    	if(INSTANCE != null){
        	throws DuplicatedSingletonInstanceException; // 싱글톤 법칙 위배 익셉션 (직접 만든 익셉션)
        }
    }
}

static final 멤버 변수에 인스턴스를 저장해두고 외부에서 직접 접근할 수 있다. 리플렉션 API로 생성자를 호출할 경우를 대비해 생성자가 예외를 던질 수 있게 해야 한다. 위 코드같은 경우는 인스턴스 멤버변수가 null이 아닐 경우 두 번째 객체가 생성되려 한다고 간주해 DuplicatedSingletonInstanceException을 던진다.

 

2) public static인 정적 팩터리 메서드로 인스턴스에 접근하는 방식

public Class{
	private static final Class INSTANCE = new Class();
   
    private Class{ ... 생성자는 숨긴다 ...}
    public static Class getInstance(){ return INSTANCE; } // 정적 팩터리 메서드
    
}

인스턴스 멤버 변수에 직접 접근하지 않고 정적 팩터리 메서드로 접근한다. 이때 팩터리 메서드는 이미 만들어진 인스턴스의 레퍼런스만 반환하기 때문에 새로운 인스턴스가 만들어지지 않는다. 항상 같은 타입만 반환하는 방법 1)과는 달리, 정적 팩터리 메서드는 제네릭 타입으로 인스턴스를 반환할 수 있다.

 

3) 원소가 하나인 열거 타입으로 선언하는 방식

public enum Class{
	INSTANCE;
}

간결하고, 바로 직렬화해도 안전하다. 단, 다른 클래스를 상속할 수 없다.

 

 

싱글턴 직렬화

싱글턴 인스턴스를 직렬화하는 것은 쉽다. Serializable 인터페이스를 구현하기만 하면 된다. 하지만 직렬화한 인스턴스를 역직렬화할 때 문제가 생긴다. 싱글턴 인스턴스가 이미 있는데 역직렬화하면서 같은 인스턴스가 또 생기기 때문이다. 이럴 경우에는 새로운 인스턴스를 무시하는 조치가 필요하다. 

 

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

 

싱글턴 클래스를 더욱 안전하게 직렬화하려면 모든 인스턴스 필드를 transient라고 선언하고 readResolve 메서드를 제공해야 한다. transient는 해당 필드의 값이 직렬화되지 않게끔하고, readResolve 메서드는 역직렬화된 인스턴스를 무시하고 기존 인스턴스를 반환해 싱글턴을 위배하지 않도록 한다. 

 

 

 

(참고)

Effective Java 3/E (2018). Joshua B(이복연 옮김). 프로그래밍 인사이트

Comments