Design Pattern

Singleton Pattern (싱글턴 패턴)

La.place 2015. 12. 17. 18:27

 Singleton Pattern(싱글턴 패턴) 이란, 단 하나만의 인스턴스를 만들어 어디서든 접근할 수 있도록 만든 패턴 유형이다. 

 싱글턴에도 종류가 어려가지 있지만, 가장 기본적인 싱글턴 유형부터 살펴보기로 하자.

[그림] Basic Singleton

 싱글턴 패턴의 구현자체는 어렵지 않다. 먼저 싱글턴 클래스 안에 인스터스를 저장할 변수(sInstance)를 전역으로 선언해준다. 다음으로생성자를 private로 선언해주면 클래스 밖에서 객체의 생성이 불가능하게 된다. 따라서 이 객체의 인스턴스는 getInstance() 메소드를 처음 호출했을 때만 만들어지게 되며, 다음번부터 호출될 시에는 처음에 만들어진 인스턴스만 참조하게 된다. 실제로 같은 주소를 공유하는지 메인함수에서 toString() 함수를 호출하여 확인해보도록 하자.


[그림] 출력 결과


 위에 소개한 싱글턴은 자바로 코딩하였다. C++에서도 같은 방법으로 구현할 수 있는데 여기에 유명한 싱글턴 소스코드를 소개해 보고자 한다. 

[그림] Scoot Meyers Singleton

 위 소스 코드는 유명한 베스트셀러인 Effective C++의 저자 스콧 마이어스(Scott Meyers)가 사용한 싱글턴 패턴이다. 구글에 스콧 마이어스의 싱글턴이라 검색하면 이와 같은 코드를 찾아볼 수 있다. 자바와 다른점이라면C++문법인 복사생성자와 연산자 오버로딩을 사용해 복사와 대입을 금지시켜 주었다는 것을 알 수 있다. 부가적으로 C++11에서 사용되는 복사와 대입 금지 기법도 소개했으므로 관심있는 사람은 따로 찾아보길 바란다.


아직 싱글턴에 대해서 소개할 내용이 더 남아있다. 필자는 모 기업 기술 면접에서 이런 질문을 받은 적이 있다. 

만약 멀티 스레딩 환경에서 싱글턴 패턴을 사용할 때, 미세한 시간 차로 인스턴스가 2개 이상 만들어지는 상황이 발생할 경우 어떻게 해야 하는가?

 가능한 이야기이다. 사실 처음 소개한 기본적인 싱글턴 패턴은 멀티 스레드 환경이 아니면 문제가 되지않는다. 하지만 멀티 스레드 환경이 될 경우 이야기가 달라진다. 실제로 이러한 상황을 인의적으로 만들어 보고 해결 방법을 생각해 보자. 먼저 인위적으로 미세한 시간 차를 만들어 보자.


[그림] Time Lag 만들기

 싱글턴 클래스의 getInstance() 메소드 안에 sleep() 메소드를 사용하여 시간 공백을 만들어 준다. 나중에 쓰레드를 실행시키면 sInstance 안으로 들어온 후 쓰레드가 잠들기 때문에, 다음 쓰레드도 sInstance == null 인줄 알고 if문 안으로 들어오게 된다.


 다음으로 실제 쓰레드를 만들어 보자. 쓰레드에 관한 문법적 내용들은 생략하도록 하겠다. run() 메소드를 통해 getInstance() 메소드를 호출하여 tInstance 변수에 저장한다. 마지막으로 메인함수에서 getAdress() 메소드를 호출하여 실제 어떤 인스턴스가 저장되어 있는지 확인해보자.

[그림] 출력 결과

 출력 결과 예상과 같이 5개 모두 다른 인스턴스가 생성되어 참조되고 있다는 것을 확인할 수 있다. 이와 같은 상황은 단 하나의 인스턴스만 사용한다는 싱글턴 패턴의 개념에 위배된다. 그럼 어떻게 하면 이 문제를 해결할 수 있을까?멀티 쓰레드 하면 가장 먼저 떠오르는 단어가 동기화(Synchronized)이다. 그렇다, 쓰레드에서 인스턴스를 생성해 줄 때 동기화 처리를 해주면 문제는 해결된다. 아래 소스코드를 보도록 하자.

 

[그림] 출력 결과

getInstance() 메소드를 synchronized 키워드를 사용하여 동기화 시켜주었다. 출력 결과를 확인해보면 정상적으로 하나의 인스턴스를 참조하는 것을 확인할 수 있다.그런데 메소드를 동기화 시켜주면 동시에 메소드를 호출할 수 없기 때문에 성능적인 측면(특히 속도)에서 문제가 생길 수 있다. 그렇다면 다른 방법은 없을까? 

있다. 정적 변수(sInstance)를 만들자마자 초기화하는 방법이다. 아래 소스코드를 보도록 하자.

[그림] 정적 바인딩

 static 변수(정적 변수)는 클래스가 생성되고 난 후 메모리에 적재되는 타이밍에 할당되므로, 위와 같이 코드를 작성할 경우 단 한번만 초기화된다. 따라서 동기화했을 때와 같은 효과를 얻을 수 있다.

 그렇다면 왜 정적 변수와 메소드를 사용하면 될 것을 굳이 싱글턴이라는 디자인 패턴을 만들어서 사용하는 것일까? 실제로 정적으로 선언하여 사용해도 똑같은 결과를 얻을 수 있다. 심지어 정적 메서드를 사용하기 때문에 성능면에서는 더 우수하다. 그러나 정적 변수와 메소드를 사용할 수 없는 상황도 존재한다. 이와 같은 상황은 각자 찾아보면서 공부하길 바란다.

'Design Pattern' 카테고리의 다른 글

빌더 패턴 Builder Pattern  (0) 2019.04.28
팩토리 메서드 패턴 Factory Method Pattern  (0) 2019.04.18
추상 팩토리 패턴 Abstract Factory Pattern  (4) 2019.04.11
State Pattern (스테이트 패턴)  (2) 2015.12.17
SOLID 원칙  (9) 2015.12.14