본문 바로가기

CS ﹒ Algorithm/DesignPatterns

디자인 패턴 (1) Singleton Pattern : 싱글톤 패턴

 

 

소프트웨어 디자인 패턴에서 싱글턴 패턴(Singleton pattern)을 따르는 클래스는, 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴한다. 이와 같은 디자인 유형을 싱글턴 패턴이라고 한다. 주로 공통된 객체를 여러개 생성해서 사용하는 DBCP(DataBase Connection Pool)와 같은 상황에서 많이 사용된다.

 

늘 대신 말해주는 Wikipedia가 오늘도 싱글턴 패턴에 대해 대신 설명해줬다.

조금만 더 친절하게 설명하자면, 시스템 런타임이나 환경 세팅 정보 등, 인스턴스가 여러 개일 때 문제가 생길 수 있는 경우 오직 인스턴스를 한 개만 만들어 제공하기 위해 싱글톤 패턴을 사용한다.

 

싱글톤 패턴은 직관적이고 이해하기 쉬운 패턴으로써, 인스턴스를 마음대로 만들지 못하게 하려면 어떤 방법을 사용해야할지 생각해보고 읽어보자. 너무 쉽기 때문에 이 글은 1분 컷이다.

 

 

 

 

 

 

1. 가장 단순한 형태

1
2
3
4
5
6
7
public class Singleton {
    private final static Singleton INSTANCE = new Singleton();
 
    public static synchronized Singleton getInstance() {
            return INSTANCE;
    }
}
cs

 

아마 정확하게는 몰라도, 여러분이 상상한 모습과 비슷한 형태였을 것이다.

미리 static 필드에 Singleton 인스턴스를 생성해놓고 스태틱 메서드에 동기화 블럭 처리까지 완료하면 끝이다.

 

"어.. 근데 이러면 매번 클래스 로더에서 자바 코드를 읽을 때마다 얘가 메모리에 한 자리 차지하는 건데, 그렇게까지 자주 쓸 생각 없는데요"라고 생각할 수도 있다.

 

게다가 메번 synchronized로 인해 멀티 쓰레드가 동시에 getInstance()에 접근하면 애플리케이션 성능에 영향을 미칠 수 있는 것은 덤이고.

 

그런 당신을 위해 다른 코드도 준비했다.

 

 

 

 

 

 

2. 가장 보편적인 형태

1
2
3
4
5
6
7
8
9
10
11
class Singleton5 {
    Singleton5() {}
 
    private static class SingletonBox {
        private static final Singleton5 INSTANCE = new Singleton5();
    }
 
    public static Singleton5 getInstance() {
        return SingletonBox.INSTANCE;
    }
}
cs

 

보다 안전하면서도 메서드 호출 전까지 메모리 공간을 차지하지 않는 전형적인 싱글턴 코드를 볼 수 있다.

참고로 이걸 보면서 "왜 위에는 메모리에 로드되고 밑에는 로드가 안된다는 거지?"라고 생각했다면 클래스 로딩 순서를 찾으러 가자.

 

그러나 사실 이 싱글톤 패턴도 완전한 불변이 아니라는 사실을 알고 있는가?

내가 이전에 작성한 글을 읽으면 그 이유를 알 수 있을 것이다.

(https://7357.tistory.com/194)

 

완벽해보였던 우리의 싱글톤 패턴도 저런 녀석한테는 맥도 추릴 수 없다.

물론 굳이 누가 저걸 건드려?라고 생각할 수도 있지만 싸이코패스 개발자를 만날지 어떻게 알겠는가.

나도 이전 직장에서 싸이코패스 직장 상사를 만나고 싶어서 만난 것이 아니였다.

 

 

 

 

 

3. 싸이코패스 개발자 대비용

1
2
3
4
5
6
7
8
9
10
11
public enum SingletonSafe {
    INSTANCE;
    int value;
 
    public int getValue() {
        return value;
    }
    public void setValue(int value) {
        this.value = value;
    }
}
cs

 

그런 싸이코패스 개발자의 대항마가 바로 Enum이다.

Reflection은 Enum을 수정할 수 없다.

 

다만 Enum은 상속도 불가능하고 위에서 static class를 사용할 때 누릴 수 있었던 지연로딩(Lazy Loading : 호출 시점에 메모리에 로드하는 것)도 누릴 수 없다.

 

싸이코패스 개발자를 대비할지, 0.1%의 불안감이 있더라도 전형적인 싱글톤 패턴을 사용할 지는 우리의 선택에 달렸다.