设计模式是众多的开发者在长期的开发过程中所总结出的针对一些一般性问题的最佳解决方案
而单例模式则是其中最为简单同时也是最为常用的几种之一,它所针对的问题是复用的全局对象的频繁创建与销毁浪费系统资源。因此,它适用于你想要合理的控制实例化数目节省系统资源的情况,它具有以下特点:
1、单例类只允许创建一个实例;
2、单例类的实例由自己来进行创建而非通过外界;
3、单例类必须向外界对象提供这一实例
以上参考自菜鸟教程:单例模式
Unity中的单例模式大致可分为这两类:
继承MonoBehaviour类和不继承MonoBehaviour类。
MonoBehaviour的子类实现单例模式的方式很简单,只需要创建一个静态的实例然后加上 该实例 = this 就可以了,代码如下:
public class Singleton : MonoBehaviour
{
public static Singleton instance; //创建静态实例
//防止外部进行额外的实例化将构造函数设为private
private Singleton()
{
}
void Awake()
{
instance = this;
}
}
构造函数的私有化可以防止外界进行额外的实例化:
不继承MonoBehaviour类的实现方式稍微复杂一些,同时也有适用于很多种不同情形的实现方式,如:饿汉式、懒汉式、静态内部类、线程安全懒汉式、双检锁等……
这里就只谈谈上边例举的前三种实现方式。
懒汉式最大的特点是只有在第一次时才进行初始化(lazy loading,极致避免内存资源浪费),实现简单,但缺点是未实现多线程安全。
代码如下:
public class Singleton
{
private static Singleton instance;
//同样地,用私有化的构造函数来防止外界进行实例化
Singleton(){}
//提供给外界进行调用实例的方法,当然,也可以使用属性来进行代替
public static Singleton GetSingleton()
{
//如果进行调用时instance为null则进行初始化
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
既然有懒汉式,那么有勤快一些的饿汉式也是理所当然的了,顾名思义,它与仅在第一次调用时才进行初始化的懒汉式不同,
在进行实例声明的时候,它就完成了初始化,
代码如下:
public class Singleton
{
//在进行实例声明的时候就完成初始化
private static Singleton instance = new Singleton();
//同样地,用私有化的构造函数限制外界进行实例化
Singleton(){}
//提供给外界进行调用实例的方法
public static Singleton GetSingleton()
{
return instance;
}
}
这么一看是不是觉得饿汉式和懒汉式差别不大,但实际上它们之间有个很大的区别,那就是
饿汉式可以实现线程安全,
因为它在进行声明的时候就已经完成了初始化,但也因此
它在不一定会使用到的单例中容易产生垃圾对象,浪费内存
静态内部类的方式在实现上比起上边两种稍微复杂一些,但同时兼顾了lazy loding和多线程安全,
代码如下:
public class Singleton
{
Singleton(){}
//提供给外界进行调用实例的方法
public static Singleton GetSingleton()
{
return InitSingleton.Instance;
}
//使用静态内部类完成初始化
private static class InitSingleton
{
public static Singleton Instance = new Singleton();
}
}
那么问题又来了,静态内部类又是如何实现兼顾两者的呢?
我们知道,静态成员的初始化,是在类进行装载的时候进行的,而在我们没有调用过GetSingleton()方法之前,InitSingleton类没有进行任何调用,
那么自然,Singleton的单例也没有进行初始化。
而多线程安全的实现方式则和饿汉式一样了;
单例模式的实现是解决了,可每个类需要实现单例的时候都这么写一下是不是有些过于麻烦呢?
那么这里,就轮到面向对象思想中的封装和继承出场了:
将单例模式封装成基类,需要实现单例模式的其他类只需要对单例模式类进行继承就行了
单例模式基类如下(以饿汉式为例):
public class Singleton<T> : MonoBehaviour where T : Component
{
protected static GameObject SLT;
protected static T instance;
public static T Instance
{
get
{
if (instance == null)
{
if (SLT == null)
{
//负责进行调用的媒介
SLT = GameObject.Find("GlobalController").gameObject;
}
instance = SLT.GetComponent<T>();
}
return instance;
}
}
protected Singleton(){}
}
子类进行继承的时候只需要以如下的形式:
但这样是无法调用到的,必须将子类的脚本挂载在进行调用的媒介物体身上,才能访问,可以用一个空物体来专门负责挂载和管理这些全局控制的单例脚本