싱글톤(Singleton) 패턴은 객체 생성 패턴 중 하나로, 어떤 클래스에 대해 단 하나의 객체만을 생성하고, 이후에는 생성된 객체를 계속해서 재사용하는 방법을 말합니다.
싱글톤 패턴은 다음과 같은 특징을 가집니다.
- 단일 인스턴스
- 해당 클래스의 인스턴스는 단 하나만 생성됩니다.
- 생성자가 private으로 선언되어 있어서 외부에서 객체를 생성할 수 없습니다.
- 전역 접근
- 생성된 인스턴스는 어디서든지 전역적으로 접근 가능합니다.
- 해당 클래스의 유일한 인스턴스에 접근하기 위한 전역적인 접근점이 제공됩니다.
싱글톤 패턴은 다음과 같은 상황에서 유용합니다.
- 시스템에서 단 하나의 객체만을 유지해야 하는 경우
- 유일한 객체를 사용하여 일관성을 유지할 수 있습니다.
- 자원이 제한적인 경우나 객체를 여러 개 생성하면 자원을 낭비하게 되는 경우
- 전역적인 객체로 사용될 경우, 여러 개의 객체를 생성하는 것이 아니라 하나의 객체를 공유하므로 메모리 사용량이 줄어듭니다.
- 객체 생성에 대한 비용과 시간을 줄일 수 있습니다.
싱글톤 패턴은 다음과 같은 단점도 가집니다.
- 전역 객체로 인해 의존성이 높아지며, 코드 유지보수가 어려워질 수 있습니다.
- 멀티스레드 환경에서 동기화 문제가 발생할 수 있습니다.
싱글톤 패턴은 전역 변수를 사용하지 않고도 객체를 전역적으로 접근할 수 있는 방법을 제공합니다.
하지만 싱글톤 패턴은 객체를 재사용하므로, 객체 상태를 공유하는 문제가 발생할 수 있습니다.
따라서 멀티스레드 환경에서는 동기화 문제에 대한 고려가 필요합니다.
다음은 싱글톤 패턴을 구현하는 방법 기본적인 방법입니다.
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
void someMethod() {
//...
}
private:
Singleton() {} // private 생성자
Singleton(const Singleton&) = delete; // 복사 생성자 삭제
Singleton& operator=(const Singleton&) = delete; // 복사 대입 연산자 삭제
~Singleton() {} // private 소멸자
};
위의 코드에서는 Singleton 클래스를 구현하고, 해당 클래스를 싱글톤 패턴으로 구현하기 위해 getInstance() 함수를 static으로 선언합니다.
이 함수는 클래스의 유일한 인스턴스를 반환하도록 구현되어 있습니다.
getInstance() 함수 내에서는 static으로 선언된 Singleton 인스턴스를 생성하고, 해당 인스턴스를 반환합니다.
이 때 static 변수의 특성을 이용하여 유일한 인스턴스가 생성되도록 합니다.
Singleton 클래스 내부의 생성자, 복사 생성자, 대입 연산자, 소멸자는 모두 private으로 선언하여 클래스 외부에서 접근하지 못하도록 합니다.
위와 같이 구현된 Singleton 클래스는 아래와 같이 사용할 수 있습니다.
Singleton& singleton = Singleton::getInstance();
singleton.someMethod();
위와 같이 Singleton 클래스의 인스턴스를 생성하고 someMethod() 함수를 호출할 수 있습니다.
해당 함수는 항상 동일한 인스턴스에서 호출되므로, 싱글톤 패턴의 특성을 만족합니다.
위 코드에서 getInstance()는 C++11 이상의 표준에서는 스레드 안전(thread-safe)합니다.
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
C++11부터는 static 지역 변수의 초기화에 대한 스레드 안전성이 보장되도록 변경되었기 때문입니다.
따라서 getInstance() 함수가 호출될 때마다 항상 동일한 인스턴스가 반환되므로, 싱글톤 패턴의 특성을 만족합니다.
하지만 C++11 이하에서는 스레드 안전성이 보장되지 않습니다.
이 경우, getInstance() 함수 내에서 스레드 안전성을 보장하도록 뮤텍스 등의 동기화 기법을 추가해야 합니다.
예를 들어, 다음과 같이 구현할 수 있습니다.
class Singleton {
public:
static Singleton& getInstance() {
std::lock_guard lock(mutex_);
static Singleton instance;
return instance;
}
void someMethod() {
//...
}
private:
Singleton() {} // private 생성자
Singleton(const Singleton&) = delete; // 복사 생성자 삭제
Singleton& operator=(const Singleton&) = delete; // 복사 대입 연산자 삭제
~Singleton() {} // private 소멸자
static std::mutex mutex_;
};
std::mutex Singleton::mutex_;
위 코드에서는 getInstance() 함수 내에서 인스턴스 생성 전에 뮤텍스를 사용하여 동기화를 보장합니다.
이를 통해 멀티 스레드 환경에서도 Singleton 클래스의 인스턴스를 안전하게 사용할 수 있습니다.
C++11 표준에서는 static 지역 변수의 초기화에 대한 스레드 안전성이 보장됩니다.
이와 관련된 내용은 C++11 표준 문서 의 8.8 [stmt.dcl] 절에서 확인할 수 있습니다.
특히 4번 항목에서는 다음과 같이 설명하고 있습니다:
"If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization."
즉, 만약 변수의 초기화가 진행 중인 동안에 동시에 변수에 접근하는 경우, 동시 실행은 초기화가 완료될 때까지 기다려야 합니다.
이를 통해 C++11 표준에서는 스레드 안전성이 보장되도록 지정되었습니다.
'소프트웨어 디자인 패턴' 카테고리의 다른 글
디자인 패턴. 팩토리 메서드(Factory Method) 패턴 (0) | 2023.03.27 |
---|---|
디자인 패턴. 생성 패턴(Creational Patterns) (0) | 2023.03.25 |
GoF의 디자인 패턴 (0) | 2023.03.24 |
디자인 패턴. MVVM (Model-View-ViewModel) (0) | 2023.03.11 |
디자인 패턴. MVP (Model-View-Presenter) (0) | 2023.03.10 |