Study/Design Pattern

싱글톤 패턴(Singleton Pattern)

해달 2022. 7. 17. 12:35

개요

싱글톤 패턴(Singleton Pattern)은 오직 한 개의 인스턴스만을 갖도록 보장하고, 이에 대한 전역적인 접근점을 제공하는 패턴이다. 구조는 아래와 같다.

싱글톤이 되려고 하는 클래스는 Singleton 클래스를 상속 받으면 된다.

구현

구현 방법은 상황에 따라 달라질 수 있으므로 아래의 코드는 정답이 아니라 참고 정도로 여겨야 한다.

 

모던 C++

template <typename T>
class Singleton abstract
{
public:
    Singleton(const Singleton&) = delete;
    Singleton(Singleton&&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    Singleton& operator=(Singleton&&) = delete;
 
    static Singleton& GetInstance()
    {
        static T instance;
 
        return instance;
    }
};
 
 
// 사용할 때는 상속을 받는다.
class GameManager : public Singleton<GameManager> { };
 

 

 

C#

public abstract class Singleton<T>
{
    private static T _instance = new T();
 
    static Singleton()
    {
 
    }    


    public static T Instance
    {
        get
        {
            return _instance;
        }
    }
}


// 사용할 때는 아래와 같이 사용한다.
public class Temp : Singleton<Temp>
{
}

장점

싱글톤 패턴이 갖는 장점은 다음과 같다.

 

1. 인스턴스의 유일성을 보장한다.

싱글톤 패턴은 인스턴스가 유일하도록 컴파일 단계에서 강제한다. 혹여 인스턴스의 개수를 늘리고자 할 때도 유연하게 바꿀 수 있다.

 

2. 게으른 초기화(Lazy Initialization)가 일어난다.

사용하지 않는다면 생성되지 않을 뿐더러 런타임에 초기화가 된다.

 

3. 어디서든 쉽게 접근할 수 있다.

전역적인 접근점을 제공하기 때문이다.

 

4. 이름 공간을 좁힌다.

전역 이름 공간 대신 클래스 이름 공간을 사용하기에 이름 공간이 더럽혀지는 것을 방지할 수 있다.

단점

싱글톤 패턴이 갖는 단점은 다음과 같다.

 

1. 결국 전역 변수다.

전역 변수보다 조금 나은 부분이 있지만 결국 전역적인 접근점을 제공하기에 전역 변수와 같다. 전역 변수는 코드 간 결합도를 높이며, 유지보수를 어렵게 한다. 또한 스레드 동기화 문제가 있다.

 

2. 게으른 초기화를 방지할 수 없다.

초기화에 오랜 시간이 걸리거나, 메모리 단편화를 방지하기 위해 메모리 풀링(Memory Pooling)을 사용하는 경우 초기화 시점을 제어해야 한다. 하지만 싱글톤 패턴에서는 불가능하다.*

*물론 정적 변수를 사용하여 우회를 할 순 있다.

 

3. 두 가지 문제를 풀려고 한다.

싱글톤 패턴은 인스턴스의 유일성도 보장함과 동시에 전역적인 접근점도 제공한다. 둘 중 하나의 문제만 해결하고자 한다면 어떻게 해야 하는가?

고려사항

싱글톤을 쓸 때는 엄청나게 좋아보이지만 사실은 굉장히 조심해서 써야 한다. 특히나 편하다는 이유로 남용되기 쉬운 패턴이다. 싱글톤을 적용하기 전 아래의 사항을 고려해보자.

 

1. 꼭 클래스가 필요한가?

특히 다른 객체를 관리하는 매니저 객체를 싱글톤으로 만드는 경우가 있는데, 이는 사실 OOP를 해칠 수 있다. 객체 자체가 판단할 수 있는 것은 객체에 캡슐화 하자.

 

2. 정적 변수를 이용해 인스턴스 개수를 제한할 수 있다.

다만 런타임에 평가한다는 게 단점이다.

 

3. 전역적인 접근점 대신에 다른 방법을 사용하자.

      • 의존성 주입을 사용하자.
        • 의존성 주입(Dependency Injection)은 매개변수를 이용해 의존성을 넘겨주는 방식이다.
      • 상위 클래스를 이용하자.
        • 상위 클래스에 정적 변수를 만들어 하위 클래스에서 사용하게 할 수 있다.
      • 이미 전역인 객체를 이용하자.
        • 기존 전역 객체에 정적 변수를 만들어 이용하게 할 수 있다.
      • 중재자 패턴을 사용하자.
        • 구체 클래스를 직접 사용하는 대신에 중재자를 두어 결합도를 낮출 수 있다.

4. 모노스테이트를 활용할 수도 있다.

모노스테이트(Monostate)는 싱글톤 패턴을 살짝 튼 것으로 모든 멤버나 혹은 필드만 정적으로 두는 것을 말한다. Unity의 Time 클래스나 Random 클래스를 생각할 수 있다. 

 

싱글톤 패턴이 꼭 필요한 경우는 생각보다 많이 없다. 싱글톤을 적용하기 전 한번 더 생각해보고, 싱글톤 인스턴스를 참조하는 경우에도 싱글톤을 직접 가져다 쓰기 보다는 다른 식으로 우회하여 결합도를 낮출 수 있을지 생각하자.

참고자료

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

템플릿 메서드 패턴(Template Method Pattern)  (0) 2022.02.03