一、设计模式中的“独孤求败”:单例模式概述
在软件设计的江湖中,单例模式宛如那位遗世独立的剑客,一生只出一剑——整个系统生命周期中只存在一个对象实例,且这个对象必须自己负责创建、管理并提供全局访问点。
它的核心意图是:“保证一个类只有一个实例,并提供一个全局访问点。”
但,听起来简单,写起来容易,真正用得好却很难。为什么?因为单例涉及到:
- 线程安全
- 延迟加载
- 生命周期管理
- 依赖注入体系的冲突
- 测试难度
二、单例的实现方式
1. 饿汉式(Eager Singleton)
public sealed class Singleton
{
private static readonly Singleton _instance = new Singleton();
private Singleton() {}
public static Singleton Instance => _instance;
}
✅ 优点:
- 实现简单
- 天然线程安全(.NET 静态构造的特性)
❌ 缺点:
- 类加载时即创建实例,无法延迟加载,可能浪费资源
2. 懒汉式 + 双重检查锁(Double-Checked Locking)
public sealed class Singleton
{
private static Singleton _instance;
private static readonly object _lock = new object();
private Singleton() {}
public static Singleton Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
_instance = new Singleton();
}
}
return _instance;
}
}
}
✅ 优点:
- 支持延迟加载
- 相对性能优化
❌ 缺点:
- 写法繁琐,容易出错
3. Lazy 懒加载方式
public sealed class Singleton
{
private static readonly Lazy<Singleton> _lazyInstance = new Lazy<Singleton>(() => new Singleton());
private Singleton() {}
public static Singleton Instance => _lazyInstance.Value;
}
✅ 优点:
- 延迟加载
- 线程安全
4. 依赖注入
现在大多数“单例”的需求用 依赖注入 + 生命周期管理(AddSingleton) 处理更合理:
services.AddSingleton<IMyService, MySingletonService>();
你获得了单例的效果,但避免了“硬编码 + 不可测试”的老路子。
三、单例适用场景(别乱用,不然日后有你哭的)
单例是“全局状态”的一种表现。以下情况可以考虑:
✅ 适用场景:
- 配置类(Configuration/Settings)
- 日志服务(Logger)
- 线程池、连接池(ConnectionPool)
- 缓存管理器(CacheProvider)
- 全局管理器类(例如窗口管理器、事件调度器)
❌ 反模式警告:滥用单例
如果你用单例来绕过依赖注入、降低模块耦合,恭喜你……未来你将收获测试地狱、代码僵化、隐藏依赖这三大苦果。
四、总结
单例,是一种对“控制力”的渴望。你不想让对象在系统中泛滥,所以你关起门来,只允许一个版本存在。它的初衷是限制,不是滥用;是约束,不是随意。