单例模式(Singleton) 单例设计模式(Singleton Design Pattern),一个类在只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模。
结构图:
适用场景:
无状态的工具类可以使用单例模式。
角色:
单例类(Singleton):负责生成单例并返回
优点:
可以保证对象的唯一性,用于解决资源竞争等场景。
缺点:
oop支持不友好。
会隐藏类之间的依赖关系(单例类不需要显示创建、不需要依赖参数传递,在函数中直接调用就可以了。如果代码比较复杂,这种调用关系就会非常隐蔽。)
代码的可测试性不好,不方便mock。
不支持有参数的构造函数
代码(C#) 饿汉式,在程序启动的时候就会创建好实例对象,优点是使用的时候不需要再加载了,缺点是如果启动时饿汉单例过多会导致程序启动过慢。饿汉式是线程安全的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class SingletonClass { private static SingletonClass _singleton = new SingletonClass(); private SingletonClass () { } public static SingletonClass GetSingletonClass () { return _singleton; } }
懒汉式,等需要使用到这个对象的时候在加载。懒汉在创建的时候需要手动加锁保证线程安全。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class SingletonClass { private static readonly object Lock = new object (); private static SingletonClass? _singleton2; private SingletonClass () { } public static SingletonClass GetSingletonClass2 () { if (_singleton2 == null ) { lock (Lock) { if (_singleton2 == null ) return _singleton2 = new SingletonClass(); else return _singleton2; } } return _singleton2; } }
静态内部类实现单例,类都有一个隐藏属性 before field init,JIT 编译器可以在首次访问一个静态字段或者一个静态/实例方法之前,或者创建类型的第一个实例之前,随便找一个时间生成调用。具体调用时机由CLR决定,它只保证访问成员之前会执行(隐式)静态构造函数,但可能会提前很早就执行。理解成静态成员会在类第一次使用之前的任何时间初始化,这种方式也是线程安全的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class SingletonStaticClass { private SingletonStaticClass () { } private static class CteateInstanceClass { static CteateInstanceClass () { } public static SingletonStaticClass Instance = new (); } public static SingletonStaticClass GetSingletonClass () { return CteateInstanceClass.Instance; } }
使用.Net 默认 DI 实现,DI 会在程序启动的时候就在根容器创建好实例。
1 2 3 4 var builder = WebApplication.CreateBuilder(args); builder.Services.AddSingleton<SingletonClass>();
最后 和静态类的区别:
单例是有实例的代表的一个对象,而静态类代表是一组常驻内存的方法。
单例对象可以被延迟初始化。而静态类总是在类被加载的时候就初始化。
单例可以继承类、接口,而静态类不能。
性能方面,静态类会比单例模式更加好。因为静态方法在编译期就完成了静态绑定。
如果类中没有保存任何的状态性质的属性,只是提供些方法就可以使用静态类。
如果在项目中要使用单例模式,建议使用 DI 来创建单例或者使用内部静态类。