Singleton avec pièges

introduction


Il existe un grand nombre de modèles et d'anti-modèles de programmation. Souvent, l'utilisation de modèles nous dicte l'expérience et la connaissance réelle d'eux-mêmes. Dans cet article, je veux discuter avec vous de l'application du modèle Singleton , à savoir son implémentation dans Net appliquée à Unity.


Singleton


Je note que j'écris du code dans une équipe, donc je prends autant de travail que possible à l'intérieur du code afin de décharger l'équipe et d'éliminer le besoin de réfléchir à certaines des difficultés de mise en œuvre de certains modèles dans Unity.


En étudiant la littérature sur Net 1 , en relation avec cette question 2, et à la suite de travaux sur plusieurs projets, la classe suivante est née:


using UnityEngine;

/// <summary>
///    .
/// </summary>
/// <typeparam name="T">,    </typeparam>
/// <remarks>
///        OnDestroy  OnApplicationQuit
///      IsAlive.     
/// ,        .
/// 
/// 
///      Awake, OnDestroy, 
/// OnApplicationQuit    
/// base.Awake()  .
/// 
///    Initialization -      
///  .
/// 
///     unity,    
///   .    ,    
///       .
/// 
///  :
///     -  "CLR via C#"
///     - Chris Dickinson "Unity 2017 Game optimization"
///</remarks>

public class Singleton<T> : MonoBehaviour where T : Singleton<T>
{

    private static T instance = null;

    private bool alive = true;

    public static T Instance
    {
        get
        {
            if (instance != null)
            {
                return instance;
            }
            else
            {
                //Find T
                T[] managers = GameObject.FindObjectsOfType<T>();
                if (managers != null)
                {
                    if (managers.Length == 1)
                    {
                        instance = managers[0];
                        DontDestroyOnLoad(instance);
                        return instance;
                    }
                    else
                    {
                        if (managers.Length > 1)
                        {
                            Debug.LogError($"Have more that one {typeof(T).Name} in scene. " +
                                            "But this is Singleton! Check project.");
                            for (int i = 0; i < managers.Length; ++i)
                            {
                                T manager = managers[i];
                                Destroy(manager.gameObject);
                            }
                        }
                    }
                }
                //create 
                GameObject go = new GameObject(typeof(T).Name, typeof(T));
                instance = go.GetComponent<T>();
        instance.Initialization();
                DontDestroyOnLoad(instance.gameObject);
                return instance;
            }
        }

        //Can be initialized externally
        set
        {
            instance = value as T;
        }
    }

    /// <summary>
    /// Check flag if need work from OnDestroy or OnApplicationExit
    /// </summary>
    public static bool IsAlive
    {
        get
        {
            if (instance == null)
                return false;
            return instance.alive;
        }
    }

    protected void Awake()
    {
        if (instance == null)
        {
            DontDestroyOnLoad(gameObject);
            instance = this as T;
            Initialization();
        }
        else
        {
            Debug.LogError($"Have more that one {typeof(T).Name} in scene. " +
                            "But this is Singleton! Check project.");
            DestroyImmediate(this);
        }
    }

    protected void OnDestroy() { alive = false; }

    protected void OnApplicationQuit() { alive = false; }

    protected virtual void Initialization() { }
}

Je vais me concentrer sur plusieurs aspects.


Création d'objets


Lorsque vous développez un projet, et plus encore en travaillant en équipe> 3 personnes, une situation survient souvent lorsque la séquence de création d'objets devient floue. Strictement parlant 3 , la séquence d'appels à Awake () est aléatoire (bien sûr, ce n'est pas entièrement vrai, et le processus peut être influencé, mais la documentation est sacrée), c'est pourquoi il est nécessaire d'éliminer cet inconvénient gênant en implémentant la propriété Instance {get;} . En conséquence, nous obtenons un accès complet au singleton à partir de Awake () d' autres classes.


, Lazy, Awake() .

4-4, , Instance{get;}.



Unity — Awake(). , , Initialization(). (KISS).



, DI SD. . .



, , OnDestroy(), OnApplicationQuit() 5:


Did you spawn new GameObjects from OnDestroy?

, , , . , IsAlive(), , . , ...



De plus en plus, j'arrive à la conclusion qu'en utilisant le paradigme Unity, il est possible de mettre en œuvre mes projets sans Singleton. Souvent, l'application de ce modèle rend votre code hautement connecté et extrêmement fragile.


Remercier.


Sources


- Richter J. "CLR via C #. Programmation sur Microsoft.NET # Framework 4.5 en C #", 2013


- https://www.codingame.com/playgrounds/1979/different-ways-to-implement-singleton-in--net-and-make-people-hate-you-along-the-way


- https://docs.unity3d.com/en/current/ScriptReference/MonoBehaviour.Awake.html


- https://ru.wikipedia.org/wiki/Design_Patterns


- Dickinson Chris "Optimisation du jeu Unity 2017, deuxième édition", 2017


All Articles