写点什么

【愚公系列】2022 年 05 月 二十三种设计模式 (五)- 单例模式 (Singleton Pattern)

作者:愚公搬代码
  • 2022 年 5 月 05 日
  • 本文字数:3455 字

    阅读完需:约 11 分钟

前言

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。

一、单例模式(Singleton Pattern)

单例模式属于创建型模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。


一个类有且仅有一个实例,并且自行实例化向整个系统提供。

二、使用步骤

角色

1、单例类(Singleton)


保证唯一并提供全局访问点的单例类。

示例


命名空间 SingletonPattern 中包含 7 个单例类,本案例将介绍这 7 种常见的单例实现方法。


public sealed class Singleton {
private static Singleton _instance = null;
public static Singleton GetInstance() { if(_instance == null) { _instance = new Singleton(); Console.WriteLine("Singleton.GetInstance()!"); } return _instance; }
}
复制代码


最常见的单例类,但是无法保证线程安全。因为首次运行时,n 个线程可同时到达 if(_instance == null),导致_instance 可能会被多初始化 n-1 次(有 1 次是需要初始化的)。在_instance 被初始化之后新启动的线程不会使该情况重现。


public sealed class SingletonSafe {
private static SingletonSafe _instance = null;
private static readonly object _lock = new object();
public static SingletonSafe GetInstance() { lock (_lock) { if (_instance == null) { _instance = new SingletonSafe(); Console.WriteLine("SingletonSafe.GetInstance()!"); } } return _instance; }
}
复制代码


使用私有静态 object 类型的锁(微软推荐),lock 关键字会占有该锁,之后请求该锁的其它线程必需等待其释放才能进入。该方法可实现线程安全的单例模式,但是锁属于昂贵资源,“占有锁”和“释放锁”都比较耗时,并会在一定程度上阻止其它线程的执行,会显著影响程序的并发性,所以有了下面的优化。


public sealed class SingletonSafe2 {
private static SingletonSafe2 _instance = null;
private static readonly object _lock = new object();
public static SingletonSafe2 GetInstance() { if (_instance == null) { lock (_lock) { if (_instance == null) { _instance = new SingletonSafe2(); Console.WriteLine("SingletonSafe2.GetInstance()!"); } } } return _instance; }
}
复制代码


通过优先使用 if (_instance == null)这种耗费资源较少的比较来决定是否进入锁,可大幅度提高性能。因为_instance 不为 null 时,直接返回即可。


public sealed class SingletonLazy {
private static readonly Lazy<SingletonLazy> _instance = new Lazy<SingletonLazy>(() => { Console.WriteLine("SingletonLazy.GetInstance()!"); return new SingletonLazy(); });
public static SingletonLazy GetInstance() { return _instance.Value; }
}
复制代码


带泛型的 Lazy 式单例实现,这是线程安全的,仅提供给大家参考。


public sealed class SingletonReadOnly {
private static readonly SingletonReadOnly _instance = new SingletonReadOnly();
public SingletonReadOnly() { Console.WriteLine("SingletonReadOnly.GetInstance()!"); }
public static SingletonReadOnly GetInstance() { return _instance; }
}
复制代码


静态只读式单例实现(由运行时保证唯一),这是线程安全的,仅提供给大家参考。


public abstract class SingletonGenericBase<T> where T : class, new() {
private static T _instance = null;
private static readonly object _lock = new object();
public static T GetInstance() { if (_instance == null) { lock (_lock) { if (_instance == null) { _instance = new T(); Console.WriteLine("SingletonGeneric.GetInstance()!"); } } } return _instance; }
}
public sealed class SingletonGeneric : SingletonGenericBase<Singleton> {
public SingletonGeneric() { }
}
复制代码


复杂的泛型实现,这是线程安全的,仅提供给大家参考。


public abstract class SingletonGenericBase2<T> where T : class {
private static readonly Lazy<T> _instance = new Lazy<T>(() => { var ctors = typeof(T).GetConstructors( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if (ctors.Count() != 1) throw new InvalidOperationException( String.Format("Type {0} must have exactly one constructor.", typeof(T)));
var ctor = ctors.SingleOrDefault( c => !c.GetParameters().Any() && c.IsPrivate);
if (ctor == null) throw new InvalidOperationException( String.Format("The constructor for {0} must be private and take no parameters.", typeof(T)));
Console.WriteLine("SingletonGeneric2.GetInstance()!"); return (T)ctor.Invoke(null); });
public static T GetInstance() { return _instance.Value; }
}
public sealed class SingletonGeneric2 : SingletonGenericBase2<SingletonGeneric2> {
private SingletonGeneric2() { }
}
复制代码


复杂的泛型实现,这是线程安全的,仅提供给大家参考。


public class Program {
public static void Main(string[] args) { var singleton = Singleton.GetInstance(); singleton = Singleton.GetInstance();
var singletonSafe = SingletonSafe.GetInstance(); singletonSafe = SingletonSafe.GetInstance();
var singletonSafe2 = SingletonSafe2.GetInstance(); singletonSafe2 = SingletonSafe2.GetInstance();
var singletonReadOnly = SingletonReadOnly.GetInstance(); singletonReadOnly = SingletonReadOnly.GetInstance();
var singletonLazy = SingletonLazy.GetInstance(); singletonLazy = SingletonLazy.GetInstance();
var singletonGeneric = SingletonGeneric.GetInstance(); singletonGeneric = SingletonGeneric.GetInstance();
var singletonGeneric2 = SingletonGeneric2.GetInstance(); singletonGeneric2 = SingletonGeneric2.GetInstance();
Console.ReadKey(); }
}
复制代码


以上是调用方的代码,每个 GetInstance 方法均调用 2 次以展示效果。以下是这个案例的输出结果:


Singleton.GetInstance()!SingletonSafe.GetInstance()!SingletonSafe2.GetInstance()!SingletonReadOnly.GetInstance()!SingletonLazy.GetInstance()!SingletonGeneric.GetInstance()!SingletonGeneric2.GetInstance()!
复制代码


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

总结

优点

1、单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例;2、因为类控制了实例化过程,所以类可以灵活更改实例化过程。

缺点

1、没有接口,不能继承,与单一职责原则冲突。

使用场景

1、需要频繁的进行创建和销毁的对象;2、创建对象时耗时过多或耗费资源过多,但又经常用到的对象;3、工具类对象;4、频繁访问数据库或文件的对象。

发布于: 刚刚阅读数: 4
用户头像

还未添加个人签名 2022.03.01 加入

该博客包括:.NET、Java、前端、IOS、Android、鸿蒙、Linux、物联网、网络安全、python、大数据等相关使用及进阶知识。查看博客过程中,如有任何问题,皆可随时沟通。

评论

发布
暂无评论
【愚公系列】2022 年 05 月 二十三种设计模式(五)-单例模式(Singleton Pattern)_5月月更_愚公搬代码_InfoQ写作社区