设计模式之单例模式

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等生命周期函数和协同程序。

总结

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

相关推荐
怣疯knight7 小时前
unity实现2D人物从上面踩踏敌人,敌人减血的简易方法(类似马里奥的攻击手段)
unity·游戏引擎
wyzqhhhh15 小时前
前端常见的设计模式
前端·设计模式
m0_7482336416 小时前
C++开发中的常用设计模式:深入解析与应用场景
javascript·c++·设计模式
Wind哥16 小时前
设计模式23种-C++实现
开发语言·c++·windows·设计模式
闲人编程18 小时前
Python设计模式实战:用Pythonic的方式实现单例、工厂模式
开发语言·python·单例模式·设计模式·工厂模式·codecapsule·pythonic
御承扬1 天前
编程素养提升之EffectivePython(Builder篇)
python·设计模式·1024程序员节
杯莫停丶1 天前
设计模式之:享元模式
java·设计模式·享元模式
杯莫停丶1 天前
设计模式之:组合模式
设计模式·组合模式
Hero | 柒1 天前
设计模式之建造者模式
java·设计模式·1024程序员节
周杰伦_Jay1 天前
【常用设计模式全解析】创建型模式(聚焦对象创建机制)、结构型模式(优化类与对象的组合关系)、行为型模式(规范对象间的交互行为)
设计模式·架构·开源·交互·1024程序员节