Unity 游戏框架搭建 2018 (二) 单例的模板与最佳实践 (2)

以上就是最终实现了,并且加上了线程锁,而且实现了一个用来接收初始化事件的接口 ISingleton。这个实现是在任何 C# 程序中都是通用的。其测试用例如下所示:

using QFramework; // 1.需要继承 Singleton。 // 2.需要实现非 public 的构造方法。 public class XXXManager : Singleton<XXXManager> { private XXXManager() { // to do ... } } public static void main(string[] args) { XXXManager.Instance.xxxyyyzzz(); } 小结:

这个单例的模板是平时用得比较顺手的工具了,其实现是在其他的框架中发现的,拿来直接用了。反射的部分可能会耗一些性能,但是第一次调用只会执行一次,所以放心。在 Unity 中可能会需要继承 MonoBehaviour 的单例,因为很多游戏可能会只创建一个 GameObject,用来获取 MonoBehaviour 的生命周期,这些内容会再下一节中介绍:)。

MonoBehaviour 单例的模板

上一小节讲述了如何设计 C# 单例的模板。也随之抛出了问题:

如何设计接收 MonoBehaviour 生命周期的单例的模板?

如何设计?

先分析下需求:

约束脚本实例对象的个数。

约束 GameObject 的个数。

接收 MonoBehaviour 生命周期。

销毁单例和对应的 GameObject。

首先,第一点,约束脚本实例对象的个数,这个在上一篇中已经实现了。 但是第二点,约束 GameObject 的个数,这个需求,还没有思路,只好在游戏运行时判断有多少个 GameObject 已经挂上了该脚本,然后如果个数大于1抛出错误即可。 第三点,通过继承 MonoBehaviour 实现,只要覆写相应的回调方法即可。 第四点,在脚本销毁时,把静态实例置空。 完整的代码就如下所示:

using UnityEngine; /// <summary> /// 需要使用Unity生命周期的单例模式 /// </summary> namespace QFramework { public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T> { protected static T mInstance = null; public static T Instance() { if (mInstance == null) { mInstance = FindObjectOfType<T>(); if (FindObjectsOfType<T>().Length > 1) { Debug.LogError("More than 1!"); return instance; } if (instance == null) { string instanceName = typeof(T).Name; Debug.Log ("Instance Name: " + instanceName); GameObject instanceGO = GameObject.Find(instanceName); if (instanceGO == null) instanceGO = new GameObject(instanceName); instance = instanceGO.AddComponent<T>(); DontDestroyOnLoad(instanceGO); //保证实例不会被释放 Debug.Log ("Add New Singleton " + mInstance.name + " in Game!"); } else { Debug.Log("Already exist: " + mInstance.name); } } return mInstance; } protected virtual void OnDestroy() { mInstance = null; } } }

这样一个独立的 MonoSingleton 就实现了。

小结:

目前已经实现了两种单例的模板,一种是需要接收 MonoBehaviour 生命周期的,一种是不需要接收生命周期的 C# 单例的模板,可以配合着使用。虽然不是本人实现的,但是用起来可是超级爽快,2333。

Singleton Property

文章写到这,我们已经实现了 C# 单例的模板和 MonoBehaviour 单例的模板,这两个模板已经可以满足大多数实现单例的需求了。但是偶尔还是会遇到比较奇葩的需求的。

比如这样的需求:

单例要继承其他的类,比如 Model.cs 等等。

虽然单例继承其他类是比较脏的设计,但是难免会遇到不得不继承的时候。没有最好的设计,只有最合适的设计。

解决方案:

首先要保证实现单例的类从使用方式上应该不变,还是

XXX.Instance.ABCFunc();

之前的单例的模板代码如下所示:

namespace QFramework { public abstract class Singleton<T> : ISingleton where T : Singleton<T> { protected static T mInstance; static object mLock = new object(); protected Singleton() { } public static T Instance { get { lock (mLock) { if (mInstance == null) { mInstance = SingletonCreator.CreateSingleton<T>(); } } return mInstance; } } public virtual void Dispose() { mInstance = null; } public virtual void OnSingletonInit() { } } }

按照以前的方式,如果想实现一个单例的代码应该是这样的:

using QFramework; // 1.需要继承QSingleton。 // 2.需要实现非public的构造方法。 public class XXXManager : QSingleton<XXXManager> { private XXXManager() { // to do ... } } public static void main(string[] args) { XXXManager.Instance().xxxyyyzzz(); }

如果我想 XXXManager 继承一个 BaseManager 代码就变成这样了

using QFramework; // 1.需要继承QSingleton。 // 2.需要实现非public的构造方法。 public class XXXManager : BaseManager { private XXXManager() { // to do ... } }

这样这个类就不是单例了,怎么办?
答案是通过 C# 的属性器。

using QFramework; // 1.需要继承QSingleton。 // 2.需要实现非public的构造方法。 public class XXXManager : BaseManager,ISingleton { private XXXManager() { // 不建议在这里初始化代码 } void ISingleton.OnSingletonInit() { // to do ... } public static XXXManager Instance { get { return SingletonProperty<XXXManager>.Instance; } } } public static void main(string[] args) { XXXManager.Instance.xxxyyyzzz(); }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpwzzd.html