[디자인 패턴] 싱글턴 패턴 (Singleton Pattern)

⭐ 싱글턴 패턴이란?

싱글턴 패턴은 클래스가 인스턴스를 단 하나만 갖도록 보장하며, 어디에서든 그 인스턴스에 접근할 수 있도록 전역 액세스를 제공하는 디자인 패턴입니다.

 

원래 하나의 클래스는 여러 개의 인스턴스를 생성할 수 있지만 필요에 따라 프로젝트 내에 오직 하나의 객체만이 존재해야 하는 경우가 있습니다.

예를 들어, 게임을 통합적으로 관리하는 GameManager를 비롯한 여러 매니저 클래스가 이에 해당합니다.

전반적인 게임의 정보 및 데이터를 관리하는 GameManager가 두 개 이상일 경우 같은 데이터 변수지만 서로 다른 값을 가지게 될 수도 있습니다.

이러한 경우를 방지하기 위해 가장 적합한 디자인 패턴이 바로 싱글턴 패턴입니다.

 

 

 

 

⭐ 싱글턴 패턴을 사용하는 이유?

단 하나의 클래스 인스턴스만이 필요하며, 그 인스턴스를 전역적으로 공유해야 하는 상황에서 사용됩니다.

 

 

● 데이터의 일관성을 유지

   - 모든 클래스에서 동일한 하나의 클래스를 사용하기에, 데이터를 일관성있게 유지할 수 있습니다.

   - 단 하나의 객체만을 보장하기에 유일성이 보장됩니다.

 

자원의 효율적인 관리와 성능을 보장

   - 무겁거나 자원이 많이 필요한 경우 여러개의 객체를 만들지 않고 하나의 객체를 보장하기 때문에

     자원과 메모리를 절약할 수 있습니다.

 

● 글로벌 상태 관리

   - 게임의 상태, 점수 등 전역적으로 관리해야하는 데이터를 관리하는데 적합합니다.

 

 

 

 

⭐ 싱글턴 패턴의 장점과 단점

싱글턴 패턴은 게임의 전반적인 상태와 데이터를 관리하는 등의 경우에 아주 좋은 디자인 패턴이 될 수 있습니다.

하지만 그만큼 단점도 명확하기 때문에 상황에 알맞게 사용하여야 합니다.

 

싱글턴 패턴을 사용하면 전역적으로 접근하기 편리해지기 때문에 당장에는 엄청난 편의성을 가져다 주는 디자인 패턴입니다.

하지만 꼭 필요한 상황에 사용하지 않고 남발하게 될 경우에는 각 클래스 간의 의존성을 높이고 유지보수에 큰 어려움을 주는 만큼

싱글턴의 장점과 단점을 명확하게 이해하고 사용하는 것이 중요합니다.

😀 장점

● 인스턴스가 단 하나임을 보장(유일성 보장), 중복 인스턴스 생성 위험이 없음

● 전역적으로 접근 및 제어가 가능

● 리소스 절약 및 성능적인 이점

● 생명주기 관리 용이

● 코드의 일관성 

 

 

😫 단점

● 전역적으로 접근이 가능하기 때문에 클래스간의 결합도가 증가합니다.

   이는 SOLID 원칙 중 의존성 역전 원칙(DIP)에 위배됩니다.

● 여러 클래스에서 싱글턴 객체를 참조하게 되면 테스트와 유지보수가 어려워집니다. (독립성이 떨어짐)

 

 

 

 

⭐ 제네릭을 이용한 싱글턴 클래스 구현하기

싱글턴 패턴은 단 하나의 인스턴스만을 가지고, 전역적인 접근이 가능하며 씬을 이동할 때도 오브젝트가 소멸되지 않습니다. 

이에 유의하여 제네릭을 이용한 싱글턴 클래스를 작성해보겠습니다.

 

더보기

< 제네릭을 이용하여 생성한 Singleton.cs >

//Singleton Pattern
using UnityEngine;

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T instance;

    public static T Instance  //인스턴스 프로퍼티 생성
    {
        get
        {
            if(instance == null)  //현재 인스턴스가 없으면
            {
                //현재 객체 타입을 찾아서 인스턴스에 넣기
                instance = (T)FindAnyObjectByType(typeof(T));

                if(instance == null) //맞는 타입의 객체를 못찾았을 경우
                {
                    //현재 객체 타입의 오브젝트를 새로 생성한다
                    GameObject obj = new GameObject(typeof(T).Name, typeof(T));
                    instance = obj.AddComponent<T>();
                }
            }
            return instance;
        }
    }

    private void Awake()
    {
        //instange가 없을 경우 지금 객체를 할당, 다른 instance가 있을 경우 지금 객체 소멸
        if (instance == null)
            instance = this as T;
        else if (instance != this)
            Destroy(gameObject);

        //씬 이동시 소멸되지 않도록 하기
        if (transform.parent != null && transform.root != null)
            DontDestroyOnLoad(transform.root.gameObject);
        else
            DontDestroyOnLoad(gameObject);
    }
}

 

 

위의 제네릭 싱글턴 클래스를 이용하여 실제 객체를 생성하는 코드입니다.

public class GameManager : Singleton<GameManager> { }