Search Unity

Singletons - Is it bad practice?

Discussion in 'Scripting' started by Freaking-Pingo, Jan 21, 2015.

  1. Freaking-Pingo

    Freaking-Pingo

    Joined:
    Aug 1, 2012
    Posts:
    310
    Hi uniteers,
    I am interested in discussing about singletons. Is it good or bad practice to use them for unity or does it depends on the circumstances?

    One of the things that have fueled our discussion is this thread on stack overflow:
    http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons

    Keep in mind that this discussion is not unity related, but singletons in general.

    We are not that experienced programmers ourselves, and none of us have actually developed a software outside the unity engine. We therefore believe that Unity is a seperate case, and singletons are actually good practice.

    Our personal argument for using singletons in our game is to have a global instance of a monobehaviour. What are your arguments of using singletons in your unity projects?
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,539
    Freaking-Pingo likes this.
  3. Freaking-Pingo

    Freaking-Pingo

    Joined:
    Aug 1, 2012
    Posts:
    310
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,539
    I also use Singletons, I actually have a class that I inherit my Singletons from. Because of the nature of 'unity', I wanted a way to enforce the singleton in unity, because most patterns out there I see don't actually enforce blocking the way that unity creates instances of a component/script.

    Also, I made a nice little editor script that creates a central GameObject to put singletons on by default (though they can be placed anywhere if you so desire), and manage how each singleton is dealt with on loading of a scene.

    The editor reflects out all known singletons, and has a drop down, for simple adding of new singletons to the gameobject.

    Singleton:
    https://code.google.com/p/spacepuppy-unity-framework/source/browse/trunk/SpacepuppyBase/Singleton.cs

    Singleton Editor:
    https://code.google.com/p/spacepupp...pyBaseEditor/Inspectors/SingletonInspector.cs
     
  5. sootie8

    sootie8

    Joined:
    Mar 25, 2014
    Posts:
    233
    Is it theoretically bad from an OOP viewpoint? Yes
    Is it necessary in certain circumstances to make your code cleaner and intentions clearer? Yes

    I am by no means a fan of the singleton design pattern, and can definitely work against you in the component based unity, by limiting your flexibility.

    Let me give you a real world problem I myself mulled over, when deciding upon using a singleton. I needed a health / damage manager for my main character, no big issue, there will only ever be one main player so I can just use a singleton and enemies can call said health manager, no need for getcomponent, a messaging system, or any other trickery. Then I realised, that I had planned for the player to jump into the perspective of another character and play as them during parts of the gameplay. A singleton would make this a real pain, and give me a lot less flexibility.

    However on the other hand, I know there will be one GUI manager in the game, its the design philosophy I prescribe to in this instance(others may have a different opinion on this point). A singleton would allow me to easily facilitate lots of objects calling different elements on the GUI where they are needed, be it to trigger text to be displayed, or whatever.

    To summerise, this is subjective information based on my personal experience. You should decide for yourself is probably the best answer. A simpler game, say on mobile may benefit from the less complex nature of a singleton design pattern widely implemented, whereas if you creating a more complex game, your codebase can turn into a steaming pile of spaghetti code.
     
    bowserscastle and Mycroft like this.
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    The answer is "it depends"

    For simple projects with small teams or individuals singletons are great. They allow you to access specific game manager type scripts without having to worry about passing around references. They can make your code simpler and cleaner. I note that this is for game manager type objects only. Anything that belongs to a specific game entity, such as player health, should not be a singleton. Singletons are reserved for things that are truly global in scope (like a Facebook manager), not just things that only exist once.

    For large projects with big team singletons are bad. They create all sorts of dependencies (Dependencies are bad). They also create hard to diagnose bugs, because anything can get into your singleton object and play around with it. You are far better off investigating patterns like Service Locator or Dependency Injection.
     
    bowserscastle and Mycroft like this.
  7. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    Singletons are also good for something that may some day have multiple instances, but right now only need one.
     
  8. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I'm curious to see how you would go about implementing this. Based on my knowledge this seems like the worst possible use case for a singleton. I'm keen to increase my knowledge.
     
    bowserscastle and Mycroft like this.
  9. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,539
    I'm not sure what _Daniel_ is talking about.

    But I've used the Multiton pattern with a default entry:
    http://en.wikipedia.org/wiki/Multiton_pattern

    Which you can easily turn a Singleton into.

    Basically the Multiton returns a 'default' (or the singleton version) of the multiton if you don't supply some key, and it supplies the keyed version if you pass in a key. You can think of it as a null key'd entry in the multiton. Then there is 2 'getInstance' methods, one with no parameters to return the default, and one that takes a parameter for a key'd entry.

    I do this with my 'Spawn' pooling system. A global spawn pool is created for managing all of the pooled objects, but if you need to create a special case spawn pool that exists independent of the primary one, you can (this could be useful for say a scenario like a mid-boss that needs a bunch of projectiles and little extras that aren't otherwise needed in the rest of the level).

    Then any of my spawn point scripts I have have an optional field that references which spawnpool you want. If left blank, it uses the default entry.

    You can see here:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5.  
    6. using com.spacepuppy.Collections;
    7. using com.spacepuppy.Utils;
    8.  
    9. namespace com.spacepuppy.Spawn
    10. {
    11.  
    12.     [AddComponentMenu("SpacePuppy/Spawn/Spawn Pool")]
    13.     public class SpawnPool : SPNotifyingComponent, ISerializationCallbackReceiver
    14.     {
    15.  
    16.         #region Static Multiton Interface
    17.  
    18.         private static SpawnPool _defaultPool;
    19.         private static UniqueList<SpawnPool> _pools = new UniqueList<SpawnPool>(ObjectReferenceEqualityComparer<SpawnPool>.Default);
    20.  
    21.         public static SpawnPool DefaultPool
    22.         {
    23.             get
    24.             {
    25.                 if (_defaultPool == null) CreatePrimaryPool();
    26.                 return _defaultPool;
    27.             }
    28.         }
    29.  
    30.         public static SpawnPool Pool(int index)
    31.         {
    32.             if (_defaultPool == null) CreatePrimaryPool();
    33.             return (index == 0) ? _defaultPool : _pools[index - 1];
    34.         }
    35.  
    36.         public static SpawnPool Pool(string name)
    37.         {
    38.             if (_defaultPool != null && _defaultPool.name == name) return _defaultPool;
    39.             return (from p in _pools where p.name == name select p).FirstOrDefault();
    40.         }
    41.  
    42.         public static int PoolCount { get { return _pools.Count + 1; } }
    43.  
    44.         private static void CreatePrimaryPool()
    45.         {
    46.             if (_defaultPool != null) return;
    47.  
    48.             var point = (from p in GameObject.FindObjectsOfType<SpawnPool>() orderby p.name == "Spacepuppy.PrimarySpawnPool" select p).FirstOrDefault();
    49.             if (!Object.ReferenceEquals(point, null))
    50.             {
    51.                 _defaultPool = point;
    52.             }
    53.             else
    54.             {
    55.                 var go = new GameObject("Spacepuppy.PrimarySpawnPool");
    56.                 _defaultPool = go.AddComponent<SpawnPool>();
    57.             }
    58.         }
    59.  
    60.         #endregion
    61.  
    62.         #region Fields
    63.  
    64.         [SerializeField()]
    65.         private List<PrefabCacheOptions> _registeredPrefabs = new List<PrefabCacheOptions>();
    66.         [System.NonSerialized()]
    67.         private Dictionary<GameObject, PrefabCacheOptions> _prefabToCache = new Dictionary<GameObject, PrefabCacheOptions>();
    68.  
    69.         #endregion
    70.  
    71.         #region CONSTRUCTOR
    72.  
    73.         protected override void Awake()
    74.         {
    75.             base.Awake();
    76.             if (!Object.ReferenceEquals(this, _defaultPool))
    77.             {
    78.                 _pools.Add(this);
    79.             }
    80.         }
    81.  
    82.         protected override void Start()
    83.         {
    84.             base.Start();
    85.  
    86.             foreach (var cache in _registeredPrefabs)
    87.             {
    88.                 cache.Load();
    89.             }
    90.         }
    91.  
    92.         protected override void OnDestroy()
    93.         {
    94.             base.OnDestroy();
    95.  
    96.             if (Object.ReferenceEquals(this, _defaultPool))
    97.             {
    98.                 _defaultPool = null;
    99.             }
    100.             else
    101.             {
    102.                 _pools.Remove(this);
    103.             }
    104.         }
    105.  
    106.         #endregion
    107.  
    108. ... rest of the SpawnPool code omitted.
    109.  
    Note... this doesn't actually use a hashtable, not sure why I didn't use a hashtable... eh, that can easily be altered if I feel like it at some point.
     
    Last edited: Jan 22, 2015
  10. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    I remember reading that somewhere, don't remember the details however. Googling seems to indicate that for multi-threaded applications, you man need one instance per thread.
     
  11. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,539
    You could write a Multiton where the thread is the key.

    In .Net the thread id is accessible through a static entry point. Which means you wouldn't necessarily have to pass in the thread id to get it, but have it where the 'getInstance' method bases it off the current thread.

    I've written these before for things like web services that stores a state based on the client attached.

    .Net actually also does thing for certain built in static states. For example the 'OperationContext' of a service:

    https://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontext(v=vs.110).aspx

    Though technically not thread dependent. For a thread dependent one just look at the various static entries of the System.Threading.Thread class:

    https://msdn.microsoft.com/en-us/library/system.threading.thread(v=vs.110).aspx

    (oh, what's that, Microsoft themself uses a context oriented multiton pattern? heh)
     
    Last edited: Jan 22, 2015
    bowserscastle and User340 like this.
  12. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    That's definitely an example of a bad use case. I wouldn't like it just on the principle that it means that enemies can damage the player, but can't damage anything else without having a messaging system that could just as easily target the player anyway. It just seems like a shortcut to build something that needs to be built properly anyway.

    That's probably the worst part about them, that they are appealing shortcuts. Unless you are absolutely certain that there should only be one instance at play, it's probably better to work around the issue through some other means.
     
    Mycroft likes this.
  13. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    Not controversial enough. You should've asked whether you should use static methods or singletons. Then it'd be a massacre.

    I use the singleton pattern very rarely, simply because it's very rarr that I absolutely have to enforce only one copy of an object existing. Most of the time when I even consider using one, I manage to come up with a use-case scenario where I might want to be able to have multiple copies, and if I can't, I guarantee another programmer can.

    I look at it like this: if it increases dependency and decreases flexibility (and singletons do both), I need a very specific reason for using it, or I need to find another way.

    On the other hand, I use static methods all the time for my factories...
     
  14. MonkeyCrime

    MonkeyCrime

    Joined:
    Jun 13, 2013
    Posts:
    32
    I'm going to stir the pot a little here. I spend a lot of my Unity development time thinking about what is the best approach, what is the right architecture, what would the pros do etc etc. And I never seem to finish many projects.

    I'm leaning towards BoredMormon here and will say that for small projects, singletons seem to work well. Many of the experiments in my "Failed Projects" folder have a GameManager, SoundManager, WorldManager and those are the bits that worked :) The animation, crappy graphics and lighting are what made me give up.

    This is from someone that has spent the last 10 or 15 years writing Windows and 3-tier web apps for a living. I can't think of a single time I've used a singleton in that environment, but in Unity for my simple crappy games, they *do* work. So don't get hung up on it; you'll soon know if it works or not.

    My 2c worth.
     
  15. Freaking-Pingo

    Freaking-Pingo

    Joined:
    Aug 1, 2012
    Posts:
    310
    I don't quiet understand all arguments given in this thread, could someone elaborate?

    Question 1:
    Why does using a singleton increase dependency compared to the alternative? Lets imagine we are talking about a manager of some sort. This manager could use a singleton for easier acces. The alternative I see is to take this manager and parse it from class to class. But whats the difference between:

    Code (CSharp):
    1. SingletonManager.Instance.MethodInsideManager();
    or

    Code (CSharp):
    1. SomeClass.MethodInsideClass(Manager someManager);
    The latter could also be done through an interface instead, is that where the loosely coupled coding comes in?

    Question 2:
    Some of you who believes that singletons can be replaced with a better alternative, state that you always can find a possible scenario where you actually want two instances of a given class. But I am not sure how you always can come to such a conclusion. As an example, lets say we implement a Sound Manager, whose sole purpose is to have a reference to all playing audio sources. For me, this would be an ideal case of using a singleton because 1. audio is presented all over in your code, and many different events triggers many different types of sound. An easy reference to this manager is ideal for both easy readable code and also structure. (Thats what I believe). 2. Why would anyone have a second instance of Sound Manager? If the singletons are simple enough, I don't see why you would have a multitude of those.
     
    bowserscastle and MaxPirat like this.
  16. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    Don't have time to go into all that at the moment, but a quick answer to #2:
    You might want some less centralized way of dealing with audio, or at the least could wind up splitting it into different classes. Maybe one for music, one for fx, one for speech. Suddenly your amount of singletons is multiplying. Your dependencies are multiplying. You decide you actually need seperate classes for battle music and non-battle music and dialogue music. Starting to see where this is going?

    Designing for scalability from the first will save a lot of time later. If you're working on an endless runner or something, obviously a singleton sound manager is just fine, but on larger programs you don't want to have to go back and rewrite code constantly. You want to plan for flexibility and scalability from the first.
     
    bowserscastle and Freaking-Pingo like this.
  17. Freaking-Pingo

    Freaking-Pingo

    Joined:
    Aug 1, 2012
    Posts:
    310
    Thank you, that was a very straightforward and reasonable example, which even I can understand. Good point.
     
  18. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    I don't quite see your angle on this. Most of the reason I would want a singleton there is that I do only want one instance of a class, especially if I do any amount of mixing on the fly or management of subtitles. This is something that just screams authoritarian control for me.
     
  19. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    The point is simply that while you may only want one instance of a class right now, that need can change in the future. It can often be easier to plan for something scalable from the start.

    Nothing authoritarian, I'm not saying to never use them, just to consider your use of them carefully. Or else. You do not want to make the Code Mafia angry, do you? That's some mighty nice code there, be a shame if it were to have an...accident.
     
  20. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    I wouldn't argue the "if you're on the fence, it's probably better not to" idea (pretty sure I had actually mentioned something along those lines up there). It's just that this is one of those areas where I see a singleton being nothing but beneficial, and since I'm unaware of a use case of multiple audio outputs, it's not like I see an instance where I would absolutely need multiple instances.

    If it's thought out properly, an audio manager shouldn't even be much of a dependency. Most game code should only interact with it by pushing a file and maybe a message, and it should mostly interact with the Unity API. Better yet because all audio is passed through it, it should make it easier to troubleshoot audio issues by keeping logs.