SingletonKit中的接口

SingletonKit中有一处接口定义,代码如下:

1
2
3
4
public interface ISingleton
{
void OnSingletonInit();
}

我们的标准C#单例Singleton<T>、Mono单例MonoSingleton<T>都继承了此接口,SingletonProperty<T>MonoSingletonProperty<T>都约束了T必须继承ISingleton

其中的OnSingletonInit()规定了一个单例在初始化时,使用此方法来初始化,而不是在单例的构造当中初始化。一个标准C#单例仅仅有一个私有构造函数来做一下限制即可,这个私有构造中不包含初始化的代码。需要注意的是Mono单例不能有构造,其在Awake中初始化的时间比OnSingletonInit要早,一般情况下Mono单例不要在Awake里面做初始化。

这种不要在构造函数中初始化,而是在Init方法中初始化的思想在QFramework设计阶段就有体现,不论是System、Model、Command,都包含有自己的初始化方法(InitExecute等),有效防止了构造当中初始化造成的死循环问题,通过定义一个初始化方法,并且在框架内部用一个List将这些方法对应的模块实例缓存起来,再依次调用。

为什么要有接口

常见的单例模块,只需要一个类就搞定了。

其使用方式也很简单,只需继承即可,如下所示:

1
2
public class MonoSingletonExample : MonoSingleton<MonoSingletonExample>{}
public class SingletonExample : Singleton<SingletonExample>{}

这种单例工具在需要继承其他类的时候就会收到限制。

比如我们想要MonoSingletonExample继承一个自己封装的MonoBehaviour,直接继承肯定是不行的,我们可能需要把自定义的MonoBehaviour做一个单例版本。

这样做就会导致一些冗余代码产生。

SingletonKit使用MonoSingletonPropertySingletonProperty来解决这个问题

MonoSingletonPropertySingletonProperty

使用代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using UnityEngine;
namespace QFramework
{
public class MonoSingletonExample : MonoBehaviour,ISingleton
{
public static MonoSingletonExample Instance
{
get { return MonoSingletonProperty<MonoSingletonExample>.Instance;}
}
public void OnSingletonInit()
{

}
}
}

namespace QFramework
{
public class SingletonExample : ISingleton
{
private SingletonExample(){}

public static SingletonExample Instance
{
get { return SingletonProperty<MonoSingletonExample>.Instance;}
}
public void OnSingletonInit()
{

}
}
}

在以上示例中,ISingleton是一个标记,它标记了MonoSingletonExampleSingletonExample是一个单例,经过标记的类可以通过MonoSingletonPropertySingletonProperty来创建单例。

从技术实现的角度讲,MonoSingletonPropertySingletonProperty是泛型类,我们将其泛型约束为:

1
2
3
4
5
6
7
8
public static class SingletonProperty<T> where T : class,ISingleton
{
//...
}
public static class MonoSingletonProperty<T> where T : MonoBehaviour,ISingleton
{
//...
}

这样就实现了继承了ISingleton接口的类才能使用MonoSingletonPropertySingletonProperty

OnSingletonInit方法

SingletonKit中所有的Singleton,都包含了这个方法的实现(因为它们都继承了ISingleton接口)。

为什么要使用OnSingletonInit方法初始化呢?

我们可把单例理解为有对象生命周期的静态类,对象的生命周期很简单,就是构造和析构。单例是有创建和销毁的过程的。

而静态类是全局唯一的,虽然单例不是静态类,但是作用和静态类差不多,全局访问,并且全局唯一。

一个对象的声明周期从创建实例开始,一般情况下,我们从构造函数中初始化。

这个在普通C#应用平台(.Net、Xamarin)没什么问题。但是在Unity中,我们在一个普通C#单例类(或者是普通C#类)的构造函数里面是不能调用Unity的一些API的(比如new GameObjectApplication.isPlaying等),这是因为Unity不能控制普通C#类的生命周期。

如果指定在OnSingletonInit去写一些初始化代码,就可以避免以上问题出现。而且我们也不用纠结初始化代码应该写在哪里,统统写在OnSingletonInit里就好了。