设计模式之单例模式

1. 什么是单例模式

单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

2. 单例模式的类型

单例模式有两种类型:
懒汉式 :在真正需要使用对象时才去创建该单例类对象

懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象。,否则则先执行实例化操作。

public class Singleton 
{
    
    private static Singleton singleton;
    
    private Singleton(){}
    
    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
    
}

这个方法其实是存在问题的,试想一下,如果两个线程同时判断singleton为空,那么它们都会去实例化一个Singleton对象,这就不符合单例模式只有一个实例对象的要求。所以在实例化对象时加锁。

public static Singleton getInstance() 
{
  	//线程A和线程B同时看到singleton = null,如果不为null,则直接返回singleton
    if (singleton == null)
    {  
        // 线程A或线程B获得该锁进行初始化
        synchronized(Singleton.class)
        {
            //其中一个线程进入该分支,另外一个线程则不会进入该分支
            if (singleton == null)
            {
                singleton = new Singleton();
            }
        }
    }
    return singleton;
}

饿汉式 :在类加载时已经创建好该单例对象,等待被程序使用

饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创建。

public class Singleton{
    
    private static final Singleton singleton = new Singleton();
    
    private Singleton(){}
    
    public static Singleton getInstance() {
        return singleton;
    }
}

注意上面的代码在第3行已经实例化好了一个Singleton对象在内存中,不会有多个Singleton对象实例存在类在加载时会在堆内存中创建一个Singleton对象,当类被卸载时,Singleton对象也随之消亡了。

Unity中的单例模式

Unity中各种Manager常用懒汉式单例模式设计。一般会创建一个泛型单利模式的BaseManager,让其他管理类继承BaseManager实现单例模式Manager。
不继承MonoBehaviour的单例模式

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//泛型单例模式基类
public class BaseManager<T> where T:class,new() //泛型约束,让子类必须实现公共无参构造函数
{
    private static T instance;
    public static T Instance
    {
        get
        {
            if(instance==null)
            {
                instance = new T();
            }
            return instance;
        }
    }
}


//创建UIManager继承BaseManager
public class UIManager:BaseManager<UIManager> 
{
    public UIManager()
    {
    
    }
}

不继承MonoBehaviour的BaseManager需要在子类中自定义公共类型的无参构造函数,不需要挂载到对象上。可以在其他脚本中通过new实例化出来(缺点,违反了单例模式)。为了解决这个问题,使用反射来优化,让构造函数设置为私有,不能再外部访问。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BaseManager<T> where T:class
{
    private static T instance;
    public static T Instance
    {
        get
        {
            if(instance==null)
            {
                //获取到T类的信息
                Type type = typeof(T);
                //获取到T类的非公共构造函数(成员构造函数,非公共构造函数,绑定对象,参数类型,参数列表)
                ConstructorInfo info = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic,
                                                            null,
                                                            Type.EmptyTypes,
                                                            null);
                if(info!=null)
                {
                    //调用构造函数
                    instance = info.Invoke(null) as T;
                }
                else
                {
                    Debug.Log("没有无参构造函数");
                }
            }
            return instance;
        }
    }
}

//创建UIManager继承BaseManager
public class UIManager:BaseManager<UIManager> 
{
		//子类需要实现私有构造函数
    private UIManager()
    {
    
    }
}

继承MonoBehaviour的单例模式

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//自动挂载脚本
public class BaseManager<T> : MonoBehaviour where T:MonoBehaviour//泛型约束,让子类必须继承MonoBehaviour
{
    private static T instance;
    public static T Instance
    {
        get
        {
            if(instance==null)
            {
                //创建一个对象
                GameObject obj = new GameObject();
                //将该脚本挂载到对象上
                instance = obj.AddComponent<T>();
                //为对象取名为当前类的名字
                obj.name = typeof(T).ToString();
                //跨场景不销毁对象
                DontDestroyOnLoad(obj);
            }
           
            return instance;
        }
    }
}


//创建UIManager继承BaseManager
public class UIManager:BaseManager<UIManager> 
{
	void Start()
  {
  
  }

  void Update()
  {
  
  }
}

继承MonoBehaviour的BaseManager子类不需要声明无参构造函数,需要挂载到对象上,但不能被实例化。可以执行Start,Update等生命周期函数和协同程序。

总结

单例模式是一种常见的设计模式,它让一个类只有一个实例,并提供一个全局访问点来获取该实例。单例模式适用于需要全局唯一实例的场景,如日志记录器、配置管理类等。在使用单例模式时,需要注意线程安全问题,以及避免出现多次实例化问题。

相关推荐
ke_wu5 小时前
结构型设计模式
开发语言·设计模式·组合模式·简单工厂模式·工厂方法模式·抽象工厂模式·装饰器模式
小马爱打代码5 小时前
设计模式详解(建造者模式)
java·设计模式·建造者模式
小王爱吃月亮糖5 小时前
C++的23种设计模式
开发语言·c++·qt·算法·设计模式·ecmascript
Unity_RAIN6 小时前
Unity 战斗系统中角色UI血条设计
ui·unity·游戏引擎
_im.m.z6 小时前
【设计模式学习笔记】1. 设计模式概述
笔记·学习·设计模式
先生沉默先10 小时前
unity使用代码在动画片段中添加event
unity
浅陌sss11 小时前
Unity性能优化 --- 减少OverDraw
unity·性能优化·游戏引擎
向宇it12 小时前
【从零开始入门unity游戏开发之——C#篇30】C#常用泛型数据结构类——list<T>列表、`List<T>` 和数组 (`T[]`) 的选择
java·开发语言·数据结构·unity·c#·游戏引擎·list
keep-learner12 小时前
Unity Dots理论学习-2.ECS有关的模块(1)
学习·unity·游戏引擎
bandaoyu13 小时前
【设计模式】装饰器模式(Decorator Pattern)
设计模式·装饰器模式