Search Unity

Singleton vs Static

Discussion in 'Scripting' started by Cawas, Aug 23, 2013.

  1. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    I'm cleaning up company code and removing a lot of ugly unmaintainable code I wrote myself back in the days...

    And I decided this time to go with Singletons and avoid anything `Static` as much as possible, because I've had issues with them before and removing them made the code run better. Very complex test cases, but practical test cases none the less.

    But I'm really not sure this is a wise decision and I could find no real final word on this.

    So, following up a brief discussion I've had on Answers, I wanted to know better what anyone think (and continue the argument with Mr Fattie).

    This is an instance of how I'm handling this issue now. Unless someone need, I won't get into details of auxiliary implementations as I think they speak for themselves:

    Code (csharp):
    1.  
    2. public class Manager : Singleton<Manager> {
    3.   public string reallyGlobalVar = "whatever";
    4.  
    5.   public Language language;
    6.   public Scene scene;
    7.  
    8.   void Awake () {
    9.     language = Instance.GetOrAddComponent<Language>();
    10.     scene = Instance.GetOrAddComponent<Scene>();
    11.   }
    12.  
    13.   void Update () {
    14.     if (Input.GetKeyUp("escape")) { // on android, this is the "back button"
    15.       InceptionSystem.BroadcastAll("EscapeButton");
    16.     }
    17.   }
    18. }
    19.  
    20. public class Language : MonoBehaviour {
    21.   public string current;
    22.   public string lastLang;
    23. }
    24.  
    It is more inconvenient to call them, always with `Manager.Instance` but at least I think it's less prone to error and problems.

    What you think?
     
    ryandeath189 likes this.
  2. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    It really depends on your needs. You should be a little careful about thread safety but the same can be said for Static classes. But here are the big wins for using a Singleton IMHO:

    1. Singleton can implement an interface (Static cannot). This allows you to build contracts that you can use for other Singleton objects or just any other class you want to throw around.

    2. Static classes are lazy-loaded when they are first referenced, but must have an empty static constructor (or one is generated for you). Using the Singleton pattern, you do lots of neat stuff such as creating them with a static initialization method and making them immutable, or however you want to control it. You can also inherit from base classes which you can't do with Static classes.

    3. Singleton--- doesn't have to be. So you can use your singleton instance but if you want for any reason to create another instance of that same class there's nothing stopping you from doing so.
     
  3. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    Hey Dustin, thanks for the reply! But, damit, I wish I could receive notifications so I'd have replied to you long ago. I'm still struggling with forums settings... Hope it will work next time. I've stumbled here serendipitously again. The only way I've ever replied to anything on forums.

    Anyway...

    Yeah, I was thinking more about reason #2 there (lazy-load part, didn't even ever think about inheritance, which should be a separated point), and just today I got to reason #1 as well. Awesome! Tells me I'm in the right path! :)

    But...

    For reason #3, I'm not following what you mean at all. What is "Singleton--- doesn't have to be"? Why would I want to create a second "singleton instance"? Isn't the name already implying it is single? And if it's a global var, the whole point of using a singleton, to me, is a kind of abstract way of making global vars in object orientation, in which they simply don't exist - but I think they should.

    To me, a proper oop way of having global vars wouldn't be singleton, but "application parameter" if that makes any sense. The whole application would be a class, and it would have parameters. So we could call `AppName.myVar` or `Application.myVar` rather than `Singleton.Instance.myVar`.

    And then I see a reason why we might want to separate those in groups, as illustrated by my example above. So, if that's the reasoning for #3, I'd still go with something like `Global.Group.myVar` (instead of `Application` just because that's already used for many other things in Unity).
     
  4. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    I guess what I meant is that you can repurpose your class. In your case you're thinking something like this I believe:

    Code (csharp):
    1.  
    2. public class SomeClass
    3. {
    4.       private static SomeClass _instance;
    5.  
    6.      public SomeClass Instance
    7.      {
    8.              get
    9.              {
    10.                     if(_instance == null)
    11.                         _instance = new SomeClass();
    12.  
    13.                     return _instance;
    14.               }
    15.      }
    16.  
    17.      //Private constructor to stop instantiation outside of this class
    18.      private SomeClass(){ }
    19. }
    20.  
    Now this gives you a somewhat immutable access to an instance of SomeClass which has a private constructor so it can only instantiate itself through the Instance property and since _instance is static, it's shared across all implementations. However, you could also implement a singleton from a factory of sorts. So your class would look like this:

    Code (csharp):
    1.  
    2. public class SomeClass
    3. {
    4.    //Your logic here
    5.  
    6. }
    7.  
    And then you'd have your "Globals" class or whatever you want to call it:

    Code (csharp):
    1.  
    2. public static class Globals
    3. {
    4.      private SomeClass _someClassInstance;
    5.  
    6.      public SomeClass SomeClassInstance
    7.      {
    8.            if(_someClassInstance == null)
    9.                 _someClassInstance = new SomeClass();
    10.  
    11.            return _someClassInstance;
    12.      }
    13. }
    14.  
    15.  
    Now Globals.SomeClassInstance is essentially a "singleton" (even though it's not entirely) and anyone calling Globals.SomeClassInstance will always get the same instance of "SomeClass"... but in this case, SomeClass is also a standard class so if you wanted for any reason to instantiate another instance of it you could. No idea if you'd personally ever have a need to do so, but it's possible. There are reasons you might want to though.. such as if you have a need for serialization, so in your static Globals class, you may want to provide a setter for SomeClassInstance or a method to allow it be populated, so you could deserialize and repopulate it later.

    Now you're talking primarily about using something like Singleton<MyClass> which is one method of doing it using a Generic singleton. Nothing wrong with this approach, but again, you still have the ability to have more than one true instance since your Singleton class still has to be able to call new T();. The benefit with using factory or a static instance method is that you can support parameters in your constructor, where as Singleton<Manager> is going to expect the Manager class or whatever you pass into it to always have and use a parameterless constructor.
     
  5. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    To add to this a bit... if you really want to truly use a Singleton pattern, then you do not want to be creating other instances, but likewise you also don't want to be doing Singleton<Manager>. Instead your manager would look something like this, which also uses a lock to prevent multiple threads from creating the object more than once so you don't end up with references to multiple copies. It also uses a private constructor to prevent it from being instantiated anywhere outside of itself:


    Code (csharp):
    1.  
    2. public class Manager
    3. {
    4.     //Your shared instance
    5.     private static Manager _instance = null;
    6.  
    7.     //Lock object used for thread safety
    8.     private static object _lock = new object();
    9.  
    10.    
    11.     private static Manager Instance
    12.     {
    13.        get
    14.        {
    15.             lock(_lockObject)
    16.             {
    17.                  if(_instance == null)
    18.                       _instance = new Manager(); // <-- you could use a private parameterized constructor if you wanted
    19.  
    20.                  return _instance;
    21.              }
    22.  
    23.         }
    24.     }
    25.  
    26.     //Make sure this can't be instantiated directly
    27.     private Manager(){ }
    28. }
    29.  

    You can actually use some of the same principles to create immutable classes and/or structs. If you ever want to create objects that you don't want to allow changes to, you can create a private constructor something like this:

    Code (csharp):
    1.  
    2. public struct Foo
    3. {
    4.     public string SurName { get; private set; }
    5.    
    6.     private Foo() {}
    7.  
    8.     private Foo(string surname)
    9.     {
    10.             SurName = surname;
    11.      }
    12.  
    13.      public static Foo FromSurname(string surname)
    14.      {
    15.           return new Foo(surname);
    16.       }
    17. }
    18.  
    The above allows you to impose some interesting restrictions because you've made the constructors private and you've made the setter for "SurName" private, so the class cannot be instantiated directly and the property cannot be changed. Anytime you want one you just call:

    Code (csharp):
    1.  
    2.    Foo myFoo = Foo.FromSurname("Bar");
    3.  
    If you have objects, especially value type objects, where you never expect the values to change, this is a handy pattern to use as it can save you some debugging headache down the road, especially if you're working with other developers who may have unexpectedly modified the values.
     
  6. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    Email notifications still not working! :(

    I think I see what you mean... `Global.SomeClassInstance` is indeed a singleton, but `SomeClassInstance` isn't. And a Singleton which instantiate classes that aren't singleton themselves make itself a non-pure singleton, per say. Right? Like I did (actually unintentionally) in my example on the first post.

    As for your last paragraph on the first post, well, that took me some time and help from a friend here at work. Thing I got wrong is that "Singleton is a design pattern meant to implement Global Variables into objected oriented paradigm" which isn't precisely true. I understand now it's meant to implement a class that will have a single instance. It's almost like static indeed, but not as much as global vars.

    In any case, I made some modifications based on all this. Basically 2 new lines properly commented. See what you think:

    Code (csharp):
    1.  
    2. public class Manager : Singleton<Manager> {
    3.   protected Manager () {} // guarantee this will be always a singleton only - can't use the constructor!
    4.  
    5.   public string reallyGlobalVar = "whatever";
    6.  
    7.   public Language language;
    8.   public Scene scene;
    9.  
    10.   void Awake () {
    11.     language = Instance.GetOrAddComponent<Language>();
    12.     scene = Instance.GetOrAddComponent<Scene>();
    13.   }
    14.  
    15.   void Update () {
    16.     if (Input.GetKeyUp("escape")) { // on android, this is the "back button"
    17.       InceptionSystem.BroadcastAll("EscapeButton");
    18.     }
    19.   }
    20. }
    21.  
    22. public class Language : Singleton<Language> {
    23.   protected Language () {}
    24.   new private static Language Instance { get { return null; } } // prevents calling Instance before Manager's Awake
    25.  
    26.   public string current;
    27.   public string lastLang;
    28. }
    29.  
    Finally, for the second post, well, I think the fact I wrote all this so without reading it already shows we're on the same page! :)

    I've use `{ get; protected set; }` quite a lot, it's a nice trick. And now I'll be finally cleaning up the wiki singleton with a bit more of confidence, after all these lessons. Thanks! ;-)
     
    Last edited: Aug 26, 2013
  7. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    You got it. There are multiple ways to implement a Singleton, some more "pure" than others, but it doesn't make the others any more wrong. Just use what fits your scenario best. And "private" vs "protected" is pretty much the same except that "protected" allows classes inheriting from your class to also set the property. In your case where you're wanting to create a Singleton I would recommend writing that as a sealed class anyway so you won't be tempted to inherit from it and break your pattern.
     
  8. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    Going off topic again... Woooot! After going on Forum Actions -> General Setting -> Default Thread Subscription Mode and checking my Thread Tools is "Subscribed to this thread" I finally fixed it by unsubscribing then subscribing again! Now I got notified! :D

    Also, feel free to add the Sealed or whatever improvements to the wiki as you wish. It'd be very welcomed. :p
     
    Last edited: Aug 26, 2013
  9. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Just a little critique looking at the documentation:

    "Instance" isn't a keyword, it's a property used to access the singleton instance.

    Also, I see why you can't use a true pure Singleton pattern... you're using it to implement a singleton MonoBehavior and in order for it's methods to be fired automatically, they need to be part of a GameObject, and AddComponent needs to be able to intantiate it.
     
  10. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    Yes, I know it isn't a keyword, that's why I put it in between quotes when talking about it. But, then again, I'm the worst at writing docs. I'm pretty sure you can do much better. As you can see, the best part on them are the ones I borrowed from you! :)

    I couldn't figure out any way to make the Generic Singleton a pure pattern indeed. But I think the pattern is as pure as it can get right now, with the little code hacks there. Hopefully.
     
  11. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    You can't make it a pure pattern with generics and you wouldn't be able to anyway since you're using it for MonoBehaviors. :)
     
  12. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Hello, I have a question about the wiki code, I didn't get why is it necessary to do this applicationIsQuitting thing in OnDestroy, I mean when Unity quits, or when you quit your game, everything dies, right? so.... how could you call a singleton instance then?

    I'm asking because, I'm not sure how would I adopt what I modified out of the wiki code, to use this boolean thing. My modification is simple, if more than one instance is found, destroy all instances and keep one alive:

    Code (csharp):
    1.  
    2. public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
    3. {
    4.     private static object mLock = new object();
    5.     private static T mInstance;
    6.  
    7.     public static T Instance
    8.     {
    9.         get
    10.         {
    11.             lock (mLock) {
    12.                 if (mInstance == null) {
    13.                     // try to find it
    14.                     T[] instances = FindObjectsOfType(typeof(T)) as T[];
    15.  
    16.                     // didn't find S***
    17.                     if (instances == null) {
    18.                         var instanceObj = new GameObject("(Singleton) " + typeof(T));
    19.                         mInstance = instanceObj.AddComponent<T>();
    20.                         DontDestroyOnLoad(instanceObj); // for preservation of this object through scenes
    21.                         Debug.Log("[SINGLETON]: An instance of `" + typeof(T) + "` is needed." +
    22.                                   " So gameObject `" + instanceObj.name + "` was created" +
    23.                                   " with `" + typeof(T) + "` component attached to it" +
    24.                                   " and with DontDestroyOnLoad called on it.");
    25.                     }
    26.                     else
    27.                     {
    28.                         // see if there's more than one, if so, do something about it
    29.                         if (instances.Length > 1) {
    30.                             Debug.LogWarning("[SINGLETON]: There is more than one instance of `" +
    31.                                              typeof(T) +
    32.                                              "` in your scene. Destroying all, keeping only one...");
    33.  
    34.                             for (int i = 1, len = instances.Length; i < len; i++) {
    35.                                 Destroy(instances[i]);
    36.                             }
    37.                         }
    38.                         else if (instances.Length == 1) Debug.Log("[SINGLETON]: Found only one instance of `" +
    39.                                                               typeof(T) +
    40.                                                               "` in `" + instances[0].gameObject.name +
    41.                                                               "` So singlation successful! :)");
    42.                         mInstance = instances[0];
    43.                     }
    44.                 }
    45.                 return mInstance;
    46.             }
    47.         }
    48.     }
    49. }
    50.  
    Another question, shouldn't we add some extra measures in Awake()? Like, making sure we only have one singleton. I mean, what if during at some point at run-time you decided to make a new Singleton<T>?
     
    Last edited: Sep 20, 2013
  13. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    This isn't really a true singleton pattern, but it's as close as you can get with a MonoBehavior. The Application Quitting check just prevents another instance from being created or the original instance from being returned if the application is exiting. Honestly though, I still question the need to rig a MonoBehavior to function like a singleton. It's still going to create a new instance of the MonoBehavior for each game object but it will use a shared instance. In my opinion, the singleton logic should be keep out of the MonoBehavior itself and the logic should be kept separate into a true singleton implementation that is just used by the MonoBehavior. Then you can prevent it from being instantiated and require a call to .Instance to get it.
     
  14. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    This.
     
  15. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    Wrong. I also thought that, before implementing that OnDestroy. Unity doesn't always kills everything when application quits. Not on the Editor, at very least. I think it's a bug in Unity, but in any case you can make it happen, and it's ugly. This prevents the ugliness from unwarned people. I'd hope the description there was pretty well explained, but maybe not...

    If more than one instance is found, it's better you get a warning and fix it on the code. There shouldn't be more than one. I actually have a different implementation from the wiki, and maybe I'll update the wiki with it eventually... In mine, there is no DontDestroyOnLoad within it, I use it outside when needed. So, I can have a "singleton per scene" if that makes any sense. As Dustin said, though, it's more pure to leave it as it is right now.

    I also have recently added a possibility to create the singleton from a prefab. This makes a lot of sense if you need default values to be assigned somehow within the editor.

    And there is no need for such preventive measure in Awake, since it's already there under get.

    @Dustin

    This wiki singleton won't create a new instance for each game object. I think that makes little to no sense. Maybe you were talking about vexe's one? I'm not sure.

    As for making the singleton without MonoBehaviour... I really can't recall, but I had thought of a good reason to do it so. Maybe it had something to do with controlling initialization order within it. And maybe you're right and it's just best to not do it... I gotta start writing more comments!
     
  16. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    @Cawas -

    To expand... even when you destroy a game object, your components aren't destroyed immediately. They're just "marked" to be destroyed, and at some point they are cleaned up.

    As for the game objects... what I mean is that a new component is created for every game object that accesses the Singleton. If you could just call:

    Had to think about this after I replied... I do understand why you would want to do it this way. Because you can't use generics in a MonoBehavior (for instance it couldn't be Manager<T>).
     
    Last edited: Sep 20, 2013
  17. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    I'm still not following. If I call Instance it will only create an instance if _instance is null. Else, it will just access it. How's that creating anything? Also, I think you left your edit missing some parts of your answer! :-o

    And awww, I'd hope I was wrong with using MonoBehaviour... And that removing it would also clean up the Singleton code off the need to have a OnDestroy patch... And it would still work just as good! Sad. But I'm not sure generics was my reasoning... F*** memory!
     
  18. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    OK.... you're scaring me :D - I just felt the need to use a singleton today, I've used it and it made sense to me. It made things cleaner. I liked it. And that generic thing is good as well. I haven't encountered any problems... I don't see any reason why not to use it (yet) - However @Dustin: if we try to separate the logic from the monobehaviour, for me, for what I have it's not simple because then I would have to give my singleton instance all the stuff (the logic that was inside the monobehaviour) it needs to do whatever it should do... and that requires creating extra variables and what not, unless I didn't fully get how you would go about doing that.

    Could you put some light on how is it you will separate the logic from the mono and put it in the singleton?
     
  19. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    I remembered now! Coroutines. Can't use them as singleton if it's not a monobehaviour. And can't make multiple inheritance of classes in csharp. There. This, and using prefabs (as just mentioned), to me, are good reasons enough to keep the singleton as MonoBehaviour.

    I suppose we could still make an abstract non-monobehaviour singleton, but I think there's no reason to complicate things. What would be the benefit?

    @vexe

    He just want to make the singleton implementation as pure as possible, and abstract away the MonoBehaviour component does make sense for that. But, as explained above, it wouldn't be practical.
     
  20. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    "I suppose we could still make an abstract non-monobehaviour singleton, but I think there's no reason to complicate things. What would be the benefit?"

    I was just doing that now :D that singleton you wrote works for monobehaviours, but sometimes I guess we require just plain normal singletons for classes that aren't monobehavious (if singleton suits them)
     
  21. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    The Language class in the wiki doesn't require MonoBehaviour. It's just a couple of strings. But it works pretty well as one, there's nothing lost there. So my question persists: why complicate?
     
  22. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Don't know about your exact setup and requirements, if it's not a monobeh, if it's something you only need one instance of, if it's something you need to easily access, don't those ring the singleton bell?
     
  23. Joviex

    Joviex

    Joined:
    Jan 23, 2011
    Posts:
    44
    I have never subscribed to using Singletons, and if I have, it was purely by mistake, probably in a coffee deprived zombie-esque dance.

    I actually torture new team members giving them problems that the pattern fits, then during code reviews let my senior members tell them why there are better solutions.

    I just don't believe in Object(s) without state, because decades of design tells me otherwise.

    Nikita Popov had a good write up from 2011:

    http://nikic.github.io/2011/12/27/Dont-be-STUPID-GRASP-SOLID.html

    Basically, there ain't no such thing as a single object (TANSTAASO).

    Really there isn't. Unless your code base is ridiculously tiny, and it never grows; never is an evil word in design.

    But I guess if you insist =p on using them, the wiki code goes a long way to make them thread safe at least.
     
    Last edited: Sep 21, 2013
  24. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    Sorry @vexe, I don't think it's a matter of implementation and I'm not following your bell rings.

    I'd love to read an adaptation of that link, @Joviex. I also dislike globals an singletons, for all that matters. Just couldn't think of a better approach.

    Reading the S (Singleton) of STUPID just now also didn't help me on thinking of a solution here. Granted, I've just heard of SOLID this month for the first time, DRY is first time I even see and, although I've known KISS for long, I don't really grasp the first 2 yet nor do I know of subrequests. Much the other way, all arguments used there seem to not apply here, to me.

    Here, I'll help you in helping me, and I'll bring some counter argument points which does apply here...

    1. Hardly anyone use singletons for DB connections. In my case, I don't need to "connect" to anything a second time. But, in that instance, I wouldn't change the singleton. I'd rather just add a second singleton "SecondDB" or so... Or give better, more descriptive names for both DBs. You see, if you use the singleton as "global classes" I don't think we're running away from object orientation at all.

    2. This is where I need a local example the most. "Can't extend the DB class". Why would I want to extend a singleton? To me, the whole point of a singleton is that I'd never want to extend it - you update it. Maybe my project is that small, after all... But if so, maybe there's the argument in favor of using it. Still, I don't see an alternative.

    3. Not much to argue here. Global vars are known to be evil and Singleton is known to be a global var. What I never could understand is why they're so bad. From all I've read anywhere is that they are needed. So, again, what's the alternative?

    As for the other STUPID's letters, well, seem like between static and singleton you'd choose the later, at least. Love the idea on T (Tight coupling) and reading such a simple explanation for Dependency Injection, which I've never bothered to read details about before. And Unit Testing is also a relatively new concept to me which I will apply as soon as possible. Finally, I do agree with every other letter. Easy read there. I'm still working on reading the rest now. ;-)
     
  25. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    I'm the same as you @Cawas, I'm kinda new to all this STUPID and SOLID stuff, which is why I just got GoF (Gang of four - A book about design patterns) - I think you should get it as well. I read what Nikita said about Singletons, (and other writings as well) - they all talk about the singleton being a problem, if you happen to need another instance in the future. But if you're 1000% sure that you only need one, and using a singleton for that is making things easier for you, then it's OK. We can't see what problems singleton might cause because we haven't used them enough to encounter those problems that they're talking about, and till we face them, all that 'singleton is evil' talk is kinda meaningless.
     
    Last edited: Sep 21, 2013
  26. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    @vexe I can't be 100% sure of anything. And I'm not using singleton to make things easier - it's to make things possible. I don't know another better way of doing it. As I see it, globals are needed. And sure, they should be avoided at all costs - this doesn't mean it can be completely avoided. The wiki states a very good example: Language. How would you set the application language otherwise, if you need a list of translated strings at all times?
     
  27. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Well, for me, one of the features I have in my game, is that you could "Examine" an item (just like Resident evil 1, 2, 3, etc) - Now, I only need one examine window for that, no matter how hard I try to squeeze my mind to come up with a situation that I would more than one examine window, I fail. I only need one, and I'm pretty much sure of it. Other examples would be, a SceneManager, or a GameController/Manager, etc.
     
  28. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    @vexe yeah, to me, your example would be a "scene singleton", which I did implement. It's just that same wiki singleton without the "DontDestroyOnLoad" and a few mechanisms around that idea. People also tell me "not really a singleton there" but don't point to a better solution.

    "A Singleton should be only 1 from application's beginning to end". Ok. To me, a "singleton" could be defined as "only 1 instance of that type will exist at one given time". And that works. Although right now I keep thinking "there must be a better way than friggin scene singleton", because I do like the first definition better... But again, in practice, I did came up with that scene singleton for a very good reason. Damn my memory! I need to begin taking more notes.

    All that stuff is really complicated indeed, and I think there is just no simple answer to most of them. Maybe that's the reason we still live in a Babylon, full of different languages and dialects (both in programming and in the literal meaning). Argh, becoming to philosophical already!

    @Joviex, After reading the whole page and comments, I think Gary summarize a lot of issues I had with that nikic post: http://nikic.github.io/2011/12/27/Dont-be-STUPID-GRASP-SOLID.html#comment-454610655. Now I'm off to wikipedia...

    Yeah, ok. DRY and KISS are simple enough. Can't agree more with them...
     
    Last edited: Sep 21, 2013
  29. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    I am personally using singletons instead of statics. I use singletons in a few situations. One of them is to have globally available variables that are needed by different classes. For that reason my singletons inherit MonoBehaviour, such that the variables can be edited in the inspector. For consistency reasons, I decided to keep it like that for each case. Sometimes it would make sense to have static variables instead, but that would ruin the consistency which I find preferable.
    Sure, technically those are not singletons. But who cares? It works, it is consistent, I haven't been in a situation where it wasn't usable and it's pragmatic.
    Everything can be over designed and inspected to death, but at the end of the day what matters is, that a certain solution makes sense from a technical point of view and it needs to be pragmatic.
    For my use cases it is a practical solution, but others would work too.

    Here is what I am using:

    Code (csharp):
    1. //
    2. // Author:
    3. //   Andreas Suter (andy@edelweissinteractive.com)
    4. //
    5. // Copyright (C) 2011-2013 Edelweiss Interactive (http://www.edelweissinteractive.com)
    6. //
    7.  
    8. using UnityEngine;
    9. using System.Collections;
    10.  
    11. namespace Edelweiss.Pattern {
    12.  
    13.     public abstract class Manager <G> : MonoBehaviour where G : Manager <G> {
    14.        
    15.             // Singleton
    16.         private static G s_Instance;
    17.         public static G Instance {
    18.             get {
    19.                 if (!IsInstanceSet) {
    20.                     InitializeInstance ();
    21.                     if (!IsInstanceSet) {
    22.                         throw (new System.Exception ("No " + typeof (G).Name + " found in the scene!"));
    23.                     }
    24.                 }
    25.                 return (s_Instance);
    26.             }
    27.         }
    28.        
    29.         public static bool IsInstanceSet {
    30.             get {
    31.                 return (s_Instance != null);
    32.             }
    33.         }
    34.  
    35.         private static void InitializeInstance () {
    36.             if (!IsInstanceSet) {
    37.                 G l_Instance = (G) FindObjectOfType (typeof (G));
    38.                 if (l_Instance != null) {
    39.                     SetInstance (l_Instance);
    40.                 }
    41.             }
    42.         }
    43.  
    44.         private static void SetInstance (G a_Instance) {
    45.             s_Instance = a_Instance;
    46.  
    47.             if (!s_Instance.IsDestroyedOnLoad) {
    48.                 DontDestroyOnLoad (s_Instance.gameObject);
    49.             }
    50.             s_Instance.InitializeManager ();
    51.         }
    52.        
    53.         public virtual bool IsDestroyedOnLoad {
    54.             get {
    55.                 return (true);
    56.             }
    57.         }
    58.        
    59.         private void Awake () {
    60.             if (s_Instance == null) {
    61.                 SetInstance (this as G);
    62.             } else if (s_Instance != this) {
    63.                 Debug.LogError ("More than one " + typeof (G).Name + " component found in the scene!");
    64.             }
    65.         }
    66.  
    67.         private void OnDestroy () {
    68.             if (s_Instance == this) {
    69.                 s_Instance = null;
    70.             }
    71.         }
    72.  
    73.         protected virtual void InitializeManager () {
    74.         }
    75.     }
    76. }
     
  30. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    My dear oriental co-worker pointed me to a "new" (from 2001) great solution to favor singletons, but in a smarter way: Toolbox. It's a long difficult and worthwhile read and I'll definitely be dropping my current singleton implementation and do this instead.

    I highly advice all of you (@Dustin, @vexe, @Joviex and @Dantus) to take a deep look into this. It may be obvious to many out there, but it sure wasn't for me.

    Once I've used toolbox more I eventually will update the singleton wiki to reflect this, and hopefully add a link to a unity toolbox implementation. ;-)
     
  31. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    @Cawas and @everybody, check out my new Verfices global access system right here :D - Let me know what you think...
     
  32. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Without being disrespectful in any way, I don't see a reason for something like that. From a practical point of view it looks like the problem of misusing singletons is only shifted to something else. When you request a service you need to pass an integer? I can easily misuse Verfices like that which means you didn't actually solve the singleton misuse issues.

    As a side note: Throw an exception instead of clamping the index when a service is requested.
     
  33. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Thanks for your input!

    Why not? we all have things like 'managers' in our games - I thought my approach would make things tidier - Unless you handle it in a total different way.

    How could you misuse like that?
     
  34. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    When you have well designed managers, I still don't see a need for something like that. Your approach introduces practical burdens. When I want to get a singleton or service at the beginning, it may not yet be available, because the registration didn't take place yet. That means you need to find workarounds for it, which means at the end that your elegant one liners are not anymore that useful. Sure, you may say that the services are only available in Start and not yet in Awake, but that is an unnecessary restriction for me as a programmer that should be avoided in any kind of framework.
    I know that this sounds pretty destructive. I just though about how I would use it in a practical environment and had the impression that it would not help me in my day to day development. Please don't get me wrong, there may be others that find it very useful!

    Sorry, was confused by the usage of integers, and actually I still am. When you want to use the same service from different objects, how are you supposed to do it?
     
  35. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Could be solved in 2 ways:
    1- Via a pre-load scene (mentioned in the UA answer by the end here)
    2- Going with the lazy approach (the first point, in the "good to have" 4 points in the end)

    Assume I have 2 InventoryManegers registered: and I want the first one I'd simply:

    Code (csharp):
    1.  
    2. Place1:
    3. var inven = Verfices.Request<InventoryMan>(); // if you want the first service, or Verfices.RequestAt<InventoryMan>(1); for the second
    4.  
    5. Place2:
    6. var inven = Verfices.Request<InventoryMan>(); // same thing :)
    7.  
    Most of the time you won't be needing to use anything other than the normal request - but the index-specific request is there if you want.

    Maybe you read the thread quickly?

    No problem at all! :) - I just thought I'd yield what I think is something better than singletons - Well, at least I tried...
     
    Last edited: Nov 11, 2013
  36. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Be assured that I read the post. It was a quick read though.

    There are a bunch of those discussions and at some point many agree that singletons are bad because of the usual arguments but I never saw a practical solution that got rid of those aspects without introducing new ones.

    In your code I can do that to get the service:
    Code (csharp):
    1. var inven = Verfices.Request<InventoryMan>();
    But that can be done at any place within the code. It can be used in the same ugly contexts and for the unwanted usages as a singleton which would just look slightly different:
    Code (csharp):
    1. var inven = IntentoryMan.Instance;
    I simply can't spot a difference that would be relevant for me or one that would prohibit people (including myself) from doing ugly things with it. There is a bunch of comparable frameworks, but I never understood why they existed. At the end of the day, it was still possible to do ugly stuff, but instead of doing it directly, you had to do it through a framework.

    I see it from an extremely pragmatic point of view, I am totally aware of that. I may also be missing some relevant pieces that would change my mind.
     
  37. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    It seems to me like you basically did a static Toolbox, rather than singleton. And, with that, I assume you'd bring along all the disadvantages of static against singleton which we were discussing in this thread. So, at first sight, I'd say: you haven't even read my link about Toolboxes, have you?

    I also see you very eloquently used IService instead of map. But I know nothing about either so I can't say anything about that.

    But in any case, it doesn't mean a static Toolbox is worse than singleton one in the same way static is worse than singletons. I personally would have to try both, but I'd only try yours if the singleton toolbox doesn't solve all my issues. And that will already take some time for me to implement. Maybe by june next year.
     
    Last edited: Nov 11, 2013
  38. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    Again, please, check the toolbox link.
     
    Last edited: Nov 11, 2013
  39. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    @Dantus you are right! - This is the price you have to pay when you use a static global system, they're always gonna be accessed from anywhere within the code base, if you don't like that, then the only way to get rid of it is not to deal with any static global-access system in the first place.

    @Cawas I haven't seen that toolbox thing, but in a way, my idea was to make something similar to http://gameprogrammingpatterns.com/service-locator.html - But it seems I'm still missing some parts of that pattern. Please give it a read.
     
    Last edited: Nov 11, 2013
  40. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    @Cawas, yes I remember reading that toolbox - and yes, it seems that I was leaning towards that toolbox, more than the ServiceLocator pattern.

    They all say that it's bad to use singletons/statics/whatever cause it's hard to unit test - But I mean, really, does anyone do Unit testing in Unity?
     
    Last edited: Nov 11, 2013
  41. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Yes, but you won't get rid of singletons with something like that. Indeed the contrary is the case. Any kind of singleton implementation can be used as a toolbox. And any half decent programmer probably used singletons for that in certain tasks anyway. Which programmer didn't use singletons from time to time to manage a bunch of objects? Because that is exactly what the toolbox does. For me it is just a trivial use case for singletons.
    A toolbox is not a generic solution that always makes sense and that can always be used and you won't get rid of singletons with it, because it is itself a singleton!

    Another post that sounds like it is written by a know-it-all :)
     
  42. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    Too big, I will most likely never read all that. But it does say it may not be as appropriate as a singleton, depending on your case. Although all other mentions to singleton is to compare with the global state nature of both or to say how he thinks service locator is better than singleton, except for availability.

    To me, it seems like it's a whole different thing for a whole different need. I think we can make a service locator static or singleton and it will still be a service locator. So if you need to locate a service, this might be a better next step, after choosing how to do global state: singleton or static. Makes sense?

    I sure don't, but I never heard of unit testing till few weeks ago. I will sure try to do it in the future. Why not?


    You completely missed the point. Not trying to get rid of singletons, just a better way to use them. It's an improvement upon singleton. And yes, it is a generic solution that always makes sense. Or so I hope. I still will try it...
     
    Last edited: Nov 11, 2013
  43. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    I'm still not sure why some folks are so adamantly against using singletons. Now, to be perfectly frank I still so absolutely no reason you would need to use a singleton with a Monobehavior. That's what managers and other helper classes are for.. to encapsulate that functionality. Monobehaviors are not really designed to be used with a singleton pattern which is why all of the implementations are hacky at best.

    However, if you've ever done any enterprise level development outside of Unity, you'll know that singletons are very useful and avoiding them just because you don't like them is silly. Most of what folks are using the singleton pattern for can be accomplished with static classes, but as I've stated before, there are reasons you would go with the singleton pattern such as the ability to do inheritance with manager classes (such as a base manager class with different implementations using in the singleton pattern, i.e. PhysicsManager, AnimationManager, etc).
     
  44. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Got it. I still don't believe that a toolbox can be a replacement for a singleton. It may be used in a few cases, but I would never want to use the example implementation which is base on string look up?!
     
  45. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    I like it to talk about that topic when I am in the right mood. For me, singletons can't or shouldn't be avoided. Of course they can be misused as anything else. But the cause for that is not that the singleton pattern is bad, but the design of the code is not good.

    In the context of Unity it makes a lot of sense in my opinion to combine it with MonoBehaviour. It is the easiest way to modify the values directly in the inspector, even if the implementations may not be optimal. Though some of them are quite good which is better than hacky in my opinion :) .
     
  46. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    @vexe I just found a link that might interest you about Fattie's grid: http://answers.unity3d.com/questions/17916/singletons-with-coroutines.html I now recall pretty clearly it is indeed a static toolbox. I say you should most definitely think about doing it as singleton instead, and see if it's better.

    I started this thread to get clear on the subject (singleton vs static) and to find a good global var implementation for Unity3D. To me, it's now pretty clear Singletons are much better than Static for making global vars. Much better.

    And a monobehaviour singleton makes a lot of sense in Unity. For one good instance, because we need persistent coroutines between scenes. Thus global var. Thus, singleton. If not so, how else?

    If by "manager" you mean mediator pattern, I don't see how this can be a replacement for singletons. It's a pattern to lower the coupling. While singleton is just a way to unify an object's instantiation (i.e. make a global var). You can have both or just one, they're not "coupled patterns", if you will. Although I agree singleton does promote coupling, coupling is not required to exist within singleton. I may just need a class to keep existing through the application. We shouldn't use singleton (or global vars) to communicate among classes, that's not the point. By the way, the toolbox design there approaches all this.

    You're still trying to believe in a wrong concept. Toolbox isn't a replacement - it is a singleton. Some cases will make sense using pur singletons, most cases will be better to use the toolbox. It's not for a few cases.

    And the example is just an example. You don't need (nor probably should) use string look up for that. You're cherry picking bad things from an article you don't want to have the trouble to read and interpret. You don't need to. But stop saying there's "no better solution". There is.
     
  47. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    I should have said "Many" and not "All". :) But you make a good point about modifying values. Although I guess I picture a Monobehavior as being specific to a gameobject and I still prefer the singleton approach and using state objects to maintain those settings across objects. That does create the issue of not being able to modify them in the inspector, but you could create a dummy object and script and use the setters to update the singleton instance values.
     
  48. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    If the toolbox could be used in more cases, could you give me some examples?
     
  49. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    Giving examples, specially plural, is not an easy task in this case. I will eventually give you one, very much like I did on the singleton wiki. Not today. Maybe not this year.

    You could, however, try to understand all that's already written. Read deep. Test stuff out. It's not that easy to understand, especially if you never had the issues yourself. But I'd appreciate if you could do it and give some true feedback. I can find a lot of people talking bad and good about singletons on the web, but almost nobody talking about this toolbox pattern. Which is weird, because toolbox does make a lot of sense and it will sure be my next step on our project.

    The problem we have with this "MonoBehaviour Singleton" began when we needed a "scene singleton". It would only be created and used within the scene. Eventually we would need to call something from the scene singleton while the scene wasn't loaded, and it would break everything. We could solve this many ways. For one, as Dustin said, we could use a dummy. Or "simply" a better architecture might suffice (making a perfect or even good architecture is not truly that simple, tho). Currently, we are just avoiding calling it, by hand (a slightly better architecture solution).

    The toolbox solves this as well. Instead of having a scene singleton we just register and unregister the component as needed. If something is called from outside the scope, it will bring an treatable error instead of trying to instantiate a broken-placed-scene-monobehaviour-singleton. And it still can be a monobehaviour toolbox.

    I will be honest, though. Up until 3 days ago, I was sure Toolbox was by far the best answer here. Now I'm a bit in doubt, thanks to this Singleton article. But I am still tending towards toolboxes.
     
  50. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    I don't understand why you are so often referring to references that have nothing to do with Unity. Whenever I am dealing with singletons there is a at least some stuff I would like to tweak by adjusting variables. This is one of the major achievements of Unity that it is so easy to set things up and to adjust them. You can't take all the ideas that are not related to Unity and reuse them. Of course, if you give up that flexibility of Unity, you can pick ideas that are not directly targeted at Unity.

    If you have a look at the singleton I am using you'll find out that this actually solves the issue that you can't access a singleton during the initialization of a scene. Of course you can't access singletons from other scenes, but it is possible to access them even if they were not yet initialized.

    It seems you are using a singleton that is not well suited to Unity. Again, have a look at the singleton that I am using and you'll see that you can easily test whether a singleton can be accessed or not. And if you know it is in the scene, you can just access it even if it wasn't initialized yet.
    When you have any kind of MonoBehaviour singleton an automatic creation of it makes no sense to me.

    It is again a solution that is not targeted at Unity...

    I have the impression that you can solve your issues just by using a singleton/manager implementation that is targeted at Unity and that there is no need for a toolbox.