Singleton

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
m (Implementation)
(Rewrote the description and added a new alternative suggestion.)
Line 1: Line 1:
== Warning ==
+
= Alternatives =
While Singletons are great, they're also [http://programmers.stackexchange.com/questions/40373/so-singletons-are-bad-then-what/218322#218322 highly controversial] thanks to their '''abused use''' along the years. Consider using the '''[[Toolbox]]''' instead!
+
One nice alternative to the singleton pattern is the use of "ScriptableObject Variables”. These work like global variables but offer many advantages over traditional global variable and singleton concepts. Ryan Hipple from Schell Games gave an excellent presentation at Unite Austin 2017 titled [https://youtu.be/raQ3iHhE_Kk Game Architecture with Scriptable Objects] that explains how to implement them and the advantages of this technique over singletons.
  
== Introduction ==
+
Another alternative to the standard singleton is called a [[Toolbox]], which is a kind of improved singleton that encourages better coding practices.
"Why Singleton?", you may ask. First, then, what's a [http://en.wikipedia.org/wiki/Singleton_pattern singleton]? It is a design pattern that restricts the Instantiation of a class to one object. And, if you're here, you probably want to use that basically for implementing Global Variables. For any other usage, just take this as a starting place.
+
  
The advantage of using singletons, in Unity, rather than static parameters and methods, are basically:
+
= Introduction =
 +
The [http://en.wikipedia.org/wiki/Singleton_pattern singleton] pattern is a way to insure a class has only a single globally accessible instance available at all times. Behaving much like a regular static class but with some advantages. This is very useful for making global manager type classes that hold global variables and functions that many other classes need to access. However, the convenience of the pattern can easily lead to misuse and abuse and this has made it [https://softwareengineering.stackexchange.com/questions/40373/so-singletons-are-bad-then-what/218322#218322 somewhat controversial] with many critics considered it an ant-pattern that should be avoided. But like any design pattern, singletons can be very useful in certain situations and it's ultimately up to the developer to decide if it's right for them.
  
'''(1)''' Static classes are lazy-loaded when they are first referenced, but must have an empty static constructor (or one is generated for you). This means it's easier to mess up and break code if you're not careful and know what you're doing. As for using the Singleton Pattern, you automatically already do lots of neat stuff, such as creating them with a static initialization method and making them immutable.
+
==== Advantages ====
 +
* Globally accessible. No need to search for or maintain a reference to the class.
 +
* Persistent data. Can be used to maintain data across scenes.
 +
* Supports interfaces. Static classes can not implement interfaces.
 +
* Supports inheritance. Static classes can not inherent for another class.
  
'''(2)''' 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. In other words, you can have a game object with other components on it for better organization!
+
The advantage of using singletons in Unity, rather than static parameters and methods, is that static classes are lazy-loaded when they are first referenced, but must have an empty static constructor (or one is generated for you). This means it's easier to mess up and break code if you're not careful and know what you're doing. As for using the Singleton Pattern, you automatically already do lots of neat stuff, such as creating them with a static initialization method and making them immutable.
  
'''(3)''' You can also inherit from base classes, which you can't do with Static classes.
+
==== Disadvantages ====
 +
* Must use the '''''Instance''''' keyword (e.g. <ClassName>.Instance) to access the singleton class.
 +
* There can only ever be one instance of the class active at a time.
 +
* Tight connections. Modifying the singleton can easily break all other code that depends on it. Requiring a lot of refactoring.
 +
* No polymorphism.
 +
* Not very testable.
  
P.S.: Unfortunately there is no good way to remove the need of a "'''Instance''' keyword" right there, calling the singleton.
+
= Usage Example =
 +
A manager class that inherits from the Singleton base class.
 +
<syntaxhighlight lang="csharp">
 +
public class Manager : Singleton<Manager> {
 +
protected Manager () {} // guarantee this will be always a singleton only - can't use the constructor!
  
P.S.(2): This is made as MonoBehaviour because we need Coroutines. A lot of times it makes sense to leave one in a singleton, so it will persist between scenes.
+
public string myGlobalVar = "whatever";
 
+
}
== Usage Example ==
+
</syntaxhighlight>
'''MyClass.cs'''
+
A MonoBehaviour class accessing the Manager singleton.
 
<syntaxhighlight lang="csharp">
 
<syntaxhighlight lang="csharp">
 
public class MyClass : MonoBehaviour {
 
public class MyClass : MonoBehaviour {
Line 27: Line 40:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
'''Manager.cs'''
 
<syntaxhighlight lang="csharp">
 
public class Manager : Singleton<Manager> {
 
protected Manager () {} // guarantee this will be always a singleton only - can't use the constructor!
 
 
public string myGlobalVar = "whatever";
 
}
 
</syntaxhighlight>
 
 
== Implementation ==
 
  
 +
= Implementation =
 
'''Singleton.cs'''
 
'''Singleton.cs'''
 
<syntaxhighlight lang="csharp">
 
<syntaxhighlight lang="csharp">
Line 117: Line 121:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== Requirement ==
+
= Requirements =
'''MonoBehaviourExtended.cs''' (from [[GetOrAddComponent]])
+
The above script makes use of the custom extension method [[GetOrAddComponent]] which is not a part of Unity.
<syntaxhighlight lang="csharp">
+
static public class MethodExtensionForMonoBehaviourTransform {
+
/// <summary>
+
/// Gets or add a component. Usage example:
+
/// BoxCollider boxCollider = transform.GetOrAddComponent<BoxCollider>();
+
/// </summary>
+
static public T GetOrAddComponent<T> (this Component child) where T: Component {
+
T result = child.GetComponent<T>();
+
if (result == null) {
+
result = child.gameObject.AddComponent<T>();
+
}
+
return result;
+
}
+
}
+
</syntaxhighlight>
+

Revision as of 02:53, 12 November 2018

Contents

Alternatives

One nice alternative to the singleton pattern is the use of "ScriptableObject Variables”. These work like global variables but offer many advantages over traditional global variable and singleton concepts. Ryan Hipple from Schell Games gave an excellent presentation at Unite Austin 2017 titled Game Architecture with Scriptable Objects that explains how to implement them and the advantages of this technique over singletons.

Another alternative to the standard singleton is called a Toolbox, which is a kind of improved singleton that encourages better coding practices.

Introduction

The singleton pattern is a way to insure a class has only a single globally accessible instance available at all times. Behaving much like a regular static class but with some advantages. This is very useful for making global manager type classes that hold global variables and functions that many other classes need to access. However, the convenience of the pattern can easily lead to misuse and abuse and this has made it somewhat controversial with many critics considered it an ant-pattern that should be avoided. But like any design pattern, singletons can be very useful in certain situations and it's ultimately up to the developer to decide if it's right for them.

Advantages

  • Globally accessible. No need to search for or maintain a reference to the class.
  • Persistent data. Can be used to maintain data across scenes.
  • Supports interfaces. Static classes can not implement interfaces.
  • Supports inheritance. Static classes can not inherent for another class.

The advantage of using singletons in Unity, rather than static parameters and methods, is that static classes are lazy-loaded when they are first referenced, but must have an empty static constructor (or one is generated for you). This means it's easier to mess up and break code if you're not careful and know what you're doing. As for using the Singleton Pattern, you automatically already do lots of neat stuff, such as creating them with a static initialization method and making them immutable.

Disadvantages

  • Must use the Instance keyword (e.g. <ClassName>.Instance) to access the singleton class.
  • There can only ever be one instance of the class active at a time.
  • Tight connections. Modifying the singleton can easily break all other code that depends on it. Requiring a lot of refactoring.
  • No polymorphism.
  • Not very testable.

Usage Example

A manager class that inherits from the Singleton base class.

public class Manager : Singleton<Manager> {
	protected Manager () {} // guarantee this will be always a singleton only - can't use the constructor!
 
	public string myGlobalVar = "whatever";
}

A MonoBehaviour class accessing the Manager singleton.

public class MyClass : MonoBehaviour {
	void Awake () {
		Debug.Log(Manager.Instance.myGlobalVar);
	}
}


Implementation

Singleton.cs

using UnityEngine;
 
/// <summary>
/// Be aware this will not prevent a non singleton constructor
///   such as `T myT = new T();`
/// To prevent that, add `protected T () {}` to your singleton class.
/// 
/// As a note, this is made as MonoBehaviour because we need Coroutines.
/// </summary>
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
	private static T _instance;
 
	private static object _lock = new object();
 
	public static T Instance
	{
		get
		{
			if (applicationIsQuitting) {
				Debug.LogWarning("[Singleton] Instance '"+ typeof(T) +
					"' already destroyed on application quit." +
					" Won't create again - returning null.");
				return null;
			}
 
			lock(_lock)
			{
				if (_instance == null)
				{
					_instance = (T) FindObjectOfType(typeof(T));
 
					if ( FindObjectsOfType(typeof(T)).Length > 1 )
					{
						Debug.LogError("[Singleton] Something went really wrong " +
							" - there should never be more than 1 singleton!" +
							" Reopening the scene might fix it.");
						return _instance;
					}
 
					if (_instance == null)
					{
						GameObject singleton = new GameObject();
						_instance = singleton.AddComponent<T>();
						singleton.name = "(singleton) "+ typeof(T).ToString();
 
						DontDestroyOnLoad(singleton);
 
						Debug.Log("[Singleton] An instance of " + typeof(T) + 
							" is needed in the scene, so '" + singleton +
							"' was created with DontDestroyOnLoad.");
					} else {
						Debug.Log("[Singleton] Using instance already created: " +
							_instance.gameObject.name);
					}
				}
 
				return _instance;
			}
		}
	}
 
	private static bool applicationIsQuitting = false;
	/// <summary>
	/// When Unity quits, it destroys objects in a random order.
	/// In principle, a Singleton is only destroyed when application quits.
	/// If any script calls Instance after it have been destroyed, 
	///   it will create a buggy ghost object that will stay on the Editor scene
	///   even after stopping playing the Application. Really bad!
	/// So, this was made to be sure we're not creating that buggy ghost object.
	/// </summary>
	public void OnDestroy () {
		applicationIsQuitting = true;
	}
}

Requirements

The above script makes use of the custom extension method GetOrAddComponent which is not a part of Unity.

Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox