本文分享Unity中非Mono脚本单例模式和一些设计心得
在之前的文章中, 作者给大家分享了Mono脚本的单例写法, 在本文中继续和大家一起探讨普通的类如何单例化.
DCL实现
普通类的单例比起Mono类来说简单一些, 这里直接贴上实现代码:
public class Singleton<T> where T : Singleton<T>, new()
{
private static T s_Instance;
public static T instance
{
get
{
if (s_Instance == null)
{
lock (typeof(T))
{
if (s_Instance == null)
{
s_Instance = new T();
s_Instance.Init();
}
}
}
return s_Instance;
}
}
public static void CreateInstance()
{
DestroyInstance();
s_Instance = instance;
}
public static void DestroyInstance()
{
if (s_Instance == null) return;
s_Instance.Clear();
s_Instance = default(T);
}
protected void Init()
{
OnInit();
}
public void Clear()
{
OnClear();
}
protected virtual void OnInit()
{
}
protected virtual void OnClear()
{
}
}
//---------------------------------------------------------------------------------
//-- 以下是用法
public class SingletonTest : Singleton<SingletonTest>
{
protected override void OnInit()
{
Debug.LogError("[SingletonTest] init");
}
}
首先, 类的声明描述:
public class Singleton<T> where T : Singleton<T>, new()
声明一个泛型类Singleton, 对该类持有的类T进行约束: 它必须继承Singleton, 最后必须具有无参数的公共构造函数("new()"关键字描述).
注意这里的关键字**new()**必须放在最后一个位置.
Singleton也定义了一些公用的操作和一些基本的生命周期函数如OnInit和OnClear供子类重写.
心得体会
单例对象的管理
在实际开发中, 我们一般会将一些全局使用的如管理器之类的角色设计为单例, 并统一进行初始化和销毁.
而这个统一的地方, 通常是启动场景中某个不会被随着场景销毁的单例Mono脚本, 负责监听整个游戏的启动和关闭, 如MainController之类的角色.
单例类的设计是很有必要的, 而对单例对象的统一初始化和销毁也有助于对全局对象的管理.
Mono脚本与非Mono脚本的抉择
我们一般会在项目中将大部分单例类设计为非Mono的.
在某些特殊情况, 比如需要在Editor中使用到这些单例, 像是在Editor中进行资源管理, 可能会用到全局的资源管理器(ResMgr).
这个时候我们需要使用一个新的单例Mono脚本类辅助使用这个非Mono的管理器.
一般我们会定义为类似ResMgrHelper之类的角色, 它可能会用来临时负责ResMgr的初始化和更新, 这是因为我们可能会在非运行环境中使用该类或者在运行模式下通过Editor来修改单例的状态.
当然, 作者认为更好的做法是将Editor的使用单独封装成类, 而不要与正常游戏中使用的那个单例类有关联和依赖, 虽然这种情况有时是无法避免的.