C++ 디자인 패턴 - 싱글톤 패턴(Singletgon design pattern)
싱글톤은 같은 종류의 객체가 하나만 존재하도록 하며, 다른 코드의 해당 객체에 대한 단일 접근 지점을 제공하는 생성 디자인 패턴이다.
의도
싱글톤은 클래스에 인스턴스가 하나만 있도록 하면서 이 인스턴스에 대한 전역 접근지점을 제공하는 생성 디자인 패턴이다.
문제
싱글톤 패턴은 한 번에 두 가지의 문제를 동시에 해결함으로써 단일 책임 원칙을 위반한다.
1. 클래스에 인스턴스가 하나만 있도록 한다.
클래스에 있는 인스턴스 수를 제어하려는 가장 일반적인 이유는 일부 공유 리소스(데이터베이스, 파일)에 대한 접근을 제어하기 위함이다.
예를 들어 객체를 생성했지만 잠시 후 새 객체를 생성하기로 했다고 가정한다면, 새 객체를 생성하는 대신 이미 만든 객체를 받게 된다.
물론 생성자 호출은 특성상 반드시 새 객체를 반환해야 하기에 위의 행동은 일반 생성자로 구현할 수 없다.
그렇기에 클라이언트들은 항상 같은 객체와 작업하고 있다는 사실을 인식조차 못 할 수 있다.
2. 해당 인스턴스에 대한 전역 접근 지점을 제공한다.
필수 객체들을 저장하기 위해 전역 변수들을 정의했다고 가정해 본다. 이 변수들을 사용하면 매우 편리할지는 몰라도, 모든 코드가 잠재적으로 해당 변수의 내용을 덮어쓸 수 있고, 그로 인해 오류가 발생해 충돌할 수 있기에 안전한 방법은 아니다.
전역 변수와 마찬가지로 싱글톤 패턴을 사용하면 프로그램의 모든 곳에서부터 일부 객체에 접글할 수있다. 그러나 이 패턴은 다른 코드가 해당 인스턴스를 덮어쓰지 못하도록 보호하기도 한다.
이 문제에는 또 다른 측면이 있다. 첫 번째 문제를 해결하는 코드가 프로그램 전체에 흩어져 있는 것을 원하지 않으며, 특히 코드의 나머지 부분이 이미 첫 번째 문제를 해결하는 코드에 의존하고 있다면, 이 코드를 한 클래스 내에 두는 것이 훨씬 좋다.
3. 해결책
다른 객체들이 싱글톤 클래스와 함께 new 연산자를 사용하지 못하도록 디폴트 생성자를 비공개로 설정한다.
생성자 역할을 하는 정적 생성 메서드를 만들고, 내부적으로 이 메서드는 객체를 만들기 위해 비공개 생성자를 호출할 후 객체에 정적 필드에 저장한다. 이 메서드에 대한 그다음 호출들은 모두 캐시된 객체를 반환한다.
당신의 코드가 싱글톤 클래스에 접글할 수 있는 경우, 이 코드는 싱글톤의 정적 메서드를 호출할 수있다. 따라서 해당 메서드가 호출될 때마다 항상 같은 객체가 반환된다.
4. 예시
국가를 예로 들어 설명한다면, 국가는 하나의 공식 정부만 가질 수 있다.
그리고 A 정부라는 명칭은 정부를 구성하는 개인들의 신원과 관계없이 정부 책임자들의 그룹을 식별하는 글로벌 접근 지점이다.
싱글톤 클래스는 정적 메서드 GetInstance를 선언한다. 이 메서드는 자체 클래스의 같은 인스턴스를 반환한다.
싱글톤의 생성자는 항상 클라이언트 코드에서부터 숨겨져야 한다. GetInstance 메서드를 호출하는 것이 Singleton 객체를 가져올 수 있는 유일한 방법이어야 한다.
5. 구현방법
1. 싱글톤 인스턴스의 저장을 위해 클래스에 비공개 정적 필드를 추가한다.
2. 싱글톤 인스턴스를 가져오기 위한 공개된 정적 생성 메서드를 선언한다.
3. 정적 메서드 내에서 "지연된 초기화"를 구현한다, 그러면 이것은 첫 번째 호출에서 새 객체를 만든 후 그 객체를 정적 필 드에 넣고, 이 메서드는 모든 후속 호출들로부터 항상 해당 인스턴스를 반환한다.
4. 클래스의 생성자를 비공개로 만들면, 클래스의 정적 메서드는 여전히 생성자를 호출할 수 있지만 다른 객체들은 호출할 수 없게 된다.
5. 클라이언트 코드를 살펴보며 싱글톤의 생성자에 대한 모든 직접 호출들을 싱글톤의 정적 생성 메서드에 대한 호출로 바 꾼다.
싱글톤 패턴은 간단해 보이지만, 잘못 구현하거나 적절하지 않은 상황에서 사용하면 문제를 일으킬 수 있다.
따라서 필요한 경우에만 신중하게 사용하는 것이 중요하다.
6. 장단점
1.장점
1. 단일 인스턴스 보장 : 클래스의 인스턴스가 오직 하나임을 보장한다.
2. 글로벌 접근점 제공 : 인스턴스에 대한 전역 접근점을 제공한다.
3. 연산 자원의 절약 : 인스턴스를 한 번만 생성하므로 자원 낭비를 줄일 수 있다.
2.단점
1. 테스트의 어려움 : 싱글톤 객체는 전역 상태를 가지므로 단위 테스트가 어렵다.
2. 의존성 숨김 : 코드에서 다른 객체와의 의존성을 명시적으로 드러내지 않으므로 코드의 유연성이 떨어질 수 있다.
3. 멀티스레드 환경에서의 문제 : 멀티스레드 환경에서 적절히 동기화되지 않으면 인스턴스가 어러 번 생성될 수 있다.