单例模式-创建型

特点:

  1. 只存在唯一一个实例;
  2. 提供统一对外访问接口,使得全局可对该单例的唯一实例进行访问;
  3. 自行实例化(私有构造函数,不允许外界对其进行实例化)。

单例模式核心在于对于某个单例类,在系统中同时只存在唯一一个实例,并且该实例容易被外界所访问;
意味着在内存中,只存在一个实例,减少了内存开销

单例的使用时机:

  1. 资源管理器,资源对象数据的加载和卸载(无状态不需要实例化的对象);
  2. 单一客户端连接服务器等;
  3. 生命周期在游戏中永不消毁的对象。

    实现过程:

  • 包含一个静态私有字段,实例自己的类型
  • 构造方法私有
  • 包含一个静态共有方法,返回自己的类型

当做父类时,无参构造不能私有,否则子类无法使用 -> 要构造父类才能到子类

Unity的单例

极简版本

  • 利用了Mono的生命周期,脚本需要挂载
  • 该脚本组件所依附的对象在场景中必须是激活的,否则会报空指针异常
  • 不需要无参构造,利用生命周期系统已经帮我们构造且new了
  • 这里就只能GetComponent来获取单例了

    1. public class BaseManger : MonoBehaviour
    2. {
    3. private static BaseManger instance;
    4. public void Awake()
    5. {
    6. instance = this;
    7. }
    8. }

    公有静态方法版本

    直接作为单例使用

    1. public class BaseManager : MonoBehaviour
    2. {
    3. private static BaseManager instance;
    4. private BaseManager(){ } //禁止外界通过New的方式获取该类的实例
    5. public static BaseManager GetInstance()
    6. {
    7. if (instance == null)
    8. {
    9. instance = new BaseManager();
    10. }
    11. return instance;
    12. }
    13. }

    公有静态属性版本

    直接作为单例类

    1. public class BaseManager : MonoBehaviour
    2. {
    3. private static BaseManager instance;
    4. private BaseManager(){ } //禁止外界通过New的方式获取该类的实例
    5. public static BaseManager Instance
    6. {
    7. get
    8. {
    9. if (instance ==null)
    10. {
    11. instance = new BaseManager();
    12. }
    13. return instance;
    14. }
    15. }

    泛型单例基类

  • 项目较大时,Manager只管理业务逻辑

  • 不需要手动挂载到GameObject上
    1. public class Singleton<T> : MonoBehaviour where T : Component
    2. {
    3. private static T target;
    4. public static T Intance
    5. {
    6. get
    7. {
    8. target = GameObject.FindObjectOfType(typeof(T)) as T;
    9. if (target == null)
    10. {
    11. GameObject go = new GameObject();
    12. target = go.AddComponent<T>();
    13. }
    14. return target;
    15. }
    16. }
    17. }

    单例基类

    子类继承后即为单例类
    1. public class BaseManager<T> where T:new()
    2. {
    3. private static T _instance;
    4. public static T GetInstance() {
    5. if (_instance == null) {
    6. _instance = new T();
    7. }
    8. return _instance;
    9. }
    10. }
    1. public class EventCenter : BaseManager<EventCenter>
    2. {
    3. //子类继承后也为单例类
    4. }

    C#的实现

    懒汉模式(最常用)

    对象实例是在方法中才创建的
    1. public class Singleton
    2. {
    3. private static Singleton _instance = null;
    4. private Singleton() {}
    5. public static Singleton GetInstance()
    6. {
    7. if (_instance == null)
    8. {
    9. _instance = new Singleton();
    10. }
    11. return _instance;
    12. }
    13. }

    饿汉模式

    饿汉式实现就是在类加载时就创建好了,不必等到调用获取实例方法的时候才创建对象,调用方法时直接返回就可以了。
    1. public class Singleton
    2. {
    3. // 自行预先实例化,内部定义自己唯一实例,只供内部使用 //
    4. private readonly static Singleton Instance = new Singleton();
    5. private Singleton()
    6. {
    7. // Do Something
    8. }
    9. // 提供外部访问的静态方法,来对内部唯一实例进行访问 //
    10. public static Singleton GetInstance()
    11. {
    12. return Instance;
    13. }
    14. }

    懒汉和饿汉模式的区别

    1.懒汉式默认不会实例化,要等到外部调用方法时才会,饿汉式一开始就实例化了对象
    2.线程安全上,饿汉式肯定是线程安全的,因为在线程没出现之前就实例化了对象,懒汉式则是线程不安全的,因为在多线程下,如果一个线程判断完实例为null就休眠或着中断,那么另一个线程也进入方法,判断实例也为null,那么该线程就会创建实例对象,而原来的那个休眠线程恢复以后,直接就执行实例化new对象这一步,那么就会出现多个实例。
    3.分析上面的线程安全,接下来就是性能了,可能你已经想到了,饿汉式不需要加锁,执行效率高,懒汉式需要加锁,执行效率低
    4.占用内存上,饿汉式不管你用不用到它的实例对象,他一开始就已经实例化在那里了,占据了内存空间,而懒汉式等到用的时候才实例化,不会浪费内存
饿汉式 懒汉式
实例化 一开始就实例化 等到要用的时候才实例化
线程安全 安全 不安全,需要加锁
性能 效率高 效率低
占用内存 有可能会浪费 不会浪费