티스토리 뷰

유지보수시에 버그 발생을 높이고 디버깅이 힘들어지므로 되도록 멀리하는것이 좋지만 게임과 같은 특정분야에서 성능이나 효율성(소규모개발)을 이유로 Singleton Pattern이 많이 쓰이는것 같아 정리하도록 한다.

 

## 개요

MonoBehaviour나 ScriptableObject같은 Unity Object를 Singleton으로 만들수도 있지만 객체 설계 취지에서 벗어나고 객체 lifetime에 대한 제어권이 Unity C++ Engine에 있다는게 찝찝하기에 개인적으로는 사용을 지양한다.

참고로 대충 아래와 같은 방식으로 `Awake()`를 통해 초기화하해서 사용하며 안정성을 위해 부가 코드가 추가될 수 있다.

 

## MonoBehaviour Singleton

보통 싱글톤을 상속과 일반화를 통해 많이 만들겠다는 생각은 좋은 생각이 아니다.

다만, Unity 소규모 프로젝트에서 개발효율을 위해 각종 매니저들을 싱글톤으로 구성하는 경우가 있는듯하다.

이 경우 아래와 같이 사용 가능하다.

internal abstract class SingletonMonoBase<T> : MonoBehaviour
    where T : MonoBehaviour
{
    private static readonly Lazy<T> _lazy =
        new Lazy<T>(CreateInstanceOfT);

    public static T Instance => _lazy.Value;

    private static T CreateInstanceOfT()
    {
        T monobehaviourInstance = FindObjectOfType<T>();
        if (monobehaviourInstance == null)
        {
            GameObject gameObj = new GameObject(typeof(T).Name);
            monobehaviourInstance = gameObj.AddComponent<T>();
            DontDestroyOnLoad(gameObj);
        }
        return monobehaviourInstance;
    }
    
    // 필요시 Unity Message를 통한 부가적인 처리
    protected virtual void Awake() { }
    protected virtual void OnDestroy() { }
    protected virtual void OnApplicationQuit() { }
}

// 사용시
internal sealed class DerivedSingletonMono : SingletonMonoBase<DerivedSingletonMono>
{
    //protected override void Awake()
    //{
    //    base.Awake();
    //    // 필요시 부가적인 처리 ...
    //}

    //protected override void OnApplicationQuit()
    //{
    //    base.OnApplicationQuit();
    //    // ...
    //}

    //protected override void OnDestroy()
    //{
    //    base.OnDestroy();
    //    // ...
    //}
}

Lazy<T>를 통해 `Instance`프로퍼티가 호출될때 static readonly인 _lazy 멤버변수의 초기화가 `CreateInstanceOfT` static method를 통해 발생한다. Scene객체, Prefab, Addressables 및 Script 등을 통해 싱글톤으로 사용 가능하다. 몇가지 허점이 있지만 Awake, OnDestory와 같은 이벤트를 통해 필요시 보완이 가능하다.

 

## ScriptableObject Singleton (비추천)

- References 링크 참조

- ScriptableObject 개념 자체가 메모리 공유이므로 이걸 다시 싱글톤으로 만들겠다는 발상 자체가 잘못된것일 수 있다.

 

 

## Pure C#

 

보통 앱실행 시점에 모든 인스턴스를 다 로드해버리면 사용자 사용성 및 리소스 효율성 등 문제가 발생하기 때문에 Lazy instantiation(사용하는 시점에 필요하다면 인스턴스화)을 사용하기도 한다.

아래 thread-safe한 방법중 Lazy instantiation 필요성에 따라 적절하게 사용하면 될듯하다.

 

1. Lazy instantiation이 필요없는 일반적인 경우(App Domain 올라올때 인스턴스화) 

internal sealed class Singleton
{
    private static readonly Singleton _instance = new Singleton();

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

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Singleton() { }

    private Singleton() { }
}

2. Lazy instantiation이 필요한 경우 (C# 4.0 & .NET Framework 4.0 이상)

internal sealed class Singleton
{
    private static readonly Lazy<Singleton> _lazy =
        new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance { get { return _lazy.Value; } }

    private Singleton() { }
}

Lazy<T>의 경우 기본적으로 thread-safety를 지원한다.

 

2-1. 추상화 버전 (비추천)

internal abstract class SingletonBase<T> where T : class
{
    private static readonly Lazy<T> _lazy = 
        new Lazy<T>(() => Activator.CreateInstance(typeof(T), true) as T);

    public static T Instance => _lazy.Value;

    protected SingletonBase() { }
}

// 사용시
internal sealed class DerivedSingleton : SingletonBase<DerivedSingleton>
{
    DerivedSingleton() : base() { }
}

상속과 일반화까지 사용해 싱글톤을 많이 만들겠다는 소리는 설계를 뭔가 잘못하고 있다는걸 의미할수도 있다.

무차별적인 싱글톤 남용은 프로젝트 확장시 지옥을 만들 수 있으므로 추천하지 않는다.

 

3. Lazy instantiation이 필요한 경우 (C# 4.0 & .NET Framework 4.0 미만)

internal sealed class Singleton
{
    public static Singleton Instance { get { return Nested.Instance; } }
    
    private Singleton() { }

    private class Nested
    {
        internal static readonly Singleton Instance = new Singleton();
        
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested() { }
    }
}

 

 

## References

https://csharpindepth.com/articles/singleton

 

Implementing the Singleton Pattern in C#

Implementing the Singleton Pattern in C# Table of contents (for linking purposes...) The singleton pattern is one of the best-known patterns in software engineering. Essentially, a singleton is a class which only allows a single instance of itself to be cr

csharpindepth.com

https://github.com/UnityTechnologies/open-project-1/wiki

 

Home

Unity Open Project #1: Chop Chop. Contribute to UnityTechnologies/open-project-1 development by creating an account on GitHub.

github.com

https://www.youtube.com/watch?v=6kWUGEQiMUI 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함