单例模式-创建型
特点:
- 只存在唯一一个实例;
- 提供统一对外访问接口,使得全局可对该单例的唯一实例进行访问;
- 自行实例化(私有构造函数,不允许外界对其进行实例化)。
单例模式核心在于对于某个单例类,在系统中同时只存在唯一一个实例,并且该实例容易被外界所访问;
意味着在内存中,只存在一个实例,减少了内存开销
单例的使用时机:
- 包含一个静态私有字段,实例自己的类型
- 构造方法私有
- 包含一个静态共有方法,返回自己的类型
当做父类时,无参构造不能私有,否则子类无法使用 -> 要构造父类才能到子类
Unity的单例
极简版本
- 利用了Mono的生命周期,脚本需要挂载
- 该脚本组件所依附的对象在场景中必须是激活的,否则会报空指针异常
- 不需要无参构造,利用生命周期系统已经帮我们构造且new了
这里就只能GetComponent来获取单例了
public class BaseManger : MonoBehaviour
{
private static BaseManger instance;
public void Awake()
{
instance = this;
}
}
公有静态方法版本
直接作为单例使用
public class BaseManager : MonoBehaviour
{
private static BaseManager instance;
private BaseManager(){ } //禁止外界通过New的方式获取该类的实例
public static BaseManager GetInstance()
{
if (instance == null)
{
instance = new BaseManager();
}
return instance;
}
}
公有静态属性版本
直接作为单例类
public class BaseManager : MonoBehaviour
{
private static BaseManager instance;
private BaseManager(){ } //禁止外界通过New的方式获取该类的实例
public static BaseManager Instance
{
get
{
if (instance ==null)
{
instance = new BaseManager();
}
return instance;
}
}
泛型单例基类
项目较大时,Manager只管理业务逻辑
- 不需要手动挂载到GameObject上
public class Singleton<T> : MonoBehaviour where T : Component
{
private static T target;
public static T Intance
{
get
{
target = GameObject.FindObjectOfType(typeof(T)) as T;
if (target == null)
{
GameObject go = new GameObject();
target = go.AddComponent<T>();
}
return target;
}
}
}
单例基类
子类继承后即为单例类public class BaseManager<T> where T:new()
{
private static T _instance;
public static T GetInstance() {
if (_instance == null) {
_instance = new T();
}
return _instance;
}
}
public class EventCenter : BaseManager<EventCenter>
{
//子类继承后也为单例类
}
C#的实现
懒汉模式(最常用)
对象实例是在方法中才创建的public class Singleton
{
private static Singleton _instance = null;
private Singleton() {}
public static Singleton GetInstance()
{
if (_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
}
饿汉模式
饿汉式实现就是在类加载时就创建好了,不必等到调用获取实例方法的时候才创建对象,调用方法时直接返回就可以了。public class Singleton
{
// 自行预先实例化,内部定义自己唯一实例,只供内部使用 //
private readonly static Singleton Instance = new Singleton();
private Singleton()
{
// Do Something
}
// 提供外部访问的静态方法,来对内部唯一实例进行访问 //
public static Singleton GetInstance()
{
return Instance;
}
}
懒汉和饿汉模式的区别
1.懒汉式默认不会实例化,要等到外部调用方法时才会,饿汉式一开始就实例化了对象
2.线程安全上,饿汉式肯定是线程安全的,因为在线程没出现之前就实例化了对象,懒汉式则是线程不安全的,因为在多线程下,如果一个线程判断完实例为null就休眠或着中断,那么另一个线程也进入方法,判断实例也为null,那么该线程就会创建实例对象,而原来的那个休眠线程恢复以后,直接就执行实例化new对象这一步,那么就会出现多个实例。
3.分析上面的线程安全,接下来就是性能了,可能你已经想到了,饿汉式不需要加锁,执行效率高,懒汉式需要加锁,执行效率低
4.占用内存上,饿汉式不管你用不用到它的实例对象,他一开始就已经实例化在那里了,占据了内存空间,而懒汉式等到用的时候才实例化,不会浪费内存
饿汉式 | 懒汉式 | |
---|---|---|
实例化 | 一开始就实例化 | 等到要用的时候才实例化 |
线程安全 | 安全 | 不安全,需要加锁 |
性能 | 效率高 | 效率低 |
占用内存 | 有可能会浪费 | 不会浪费 |