单一模式利用 Unitys 实体 - 组件系统
核心思想是使用 GameObjects 来表示单例,这有多个优点:
- 将复杂性保持在最低限度,但支持依赖注入等概念
- 单例拥有正常的 Unity 生命周期作为实体组件系统的一部分
- 单例可以延迟加载并在常规需要的地方缓存(例如在更新循环中)
- 不需要静态字段
- 无需修改现有的 MonoBehaviours / Components 即可将其用作单例
- 容易重置(只需销毁 Singletons GameObject),下次使用时会再次加载延迟
- 易于注入模拟(在使用之前只需使用模拟初始化)
- 使用普通的 Unity 编辑器进行检查和配置,并且可以在编辑器时间发生( Unity 编辑器中可访问的 Singleton 的屏幕截图 )
Test.cs(使用示例单例):
using UnityEngine;
using UnityEngine.Assertions;
public class Test : MonoBehaviour {
void Start() {
ExampleSingleton singleton = ExampleSingleton.instance;
Assert.IsNotNull(singleton); // automatic initialization on first usage
Assert.AreEqual("abc", singleton.myVar1);
singleton.myVar1 = "123";
// multiple calls to instance() return the same object:
Assert.AreEqual(singleton, ExampleSingleton.instance);
Assert.AreEqual("123", ExampleSingleton.instance.myVar1);
}
}
ExampleSingleton.cs(包含一个示例和实际的 Singleton 类):
using UnityEngine;
using UnityEngine.Assertions;
public class ExampleSingleton : MonoBehaviour {
public static ExampleSingleton instance { get { return Singleton.get<ExampleSingleton>(); } }
public string myVar1 = "abc";
public void Start() { Assert.AreEqual(this, instance, "Singleton more than once in scene"); }
}
/// <summary> Helper that turns any MonBehaviour or other Component into a Singleton </summary>
public static class Singleton {
public static T get<T>() where T : Component {
return GetOrAddGo("Singletons").GetOrAddChild("" + typeof(T)).GetOrAddComponent<T>();
}
private static GameObject GetOrAddGo(string goName) {
var go = GameObject.Find(goName);
if (go == null) { return new GameObject(goName); }
return go;
}
}
public static class GameObjectExtensionMethods {
public static GameObject GetOrAddChild(this GameObject parentGo, string childName) {
var childGo = parentGo.transform.FindChild(childName);
if (childGo != null) { return childGo.gameObject; } // child found, return it
var newChild = new GameObject(childName); // no child found, create it
newChild.transform.SetParent(parentGo.transform, false); // add it to parent
return newChild;
}
public static T GetOrAddComponent<T>(this GameObject parentGo) where T : Component {
var comp = parentGo.GetComponent<T>();
if (comp == null) { return parentGo.AddComponent<T>(); }
return comp;
}
}
GameObject 的两个扩展方法在其他情况下也很有用,如果你不需要它们,可以在 Singleton 类中移动它们并将它们设为私有。