设计模式:单例模式(Singleton)

111 阅读2分钟

一、设计模式中的“独孤求败”:单例模式概述

在软件设计的江湖中,单例模式宛如那位遗世独立的剑客,一生只出一剑——整个系统生命周期中只存在一个对象实例,且这个对象必须自己负责创建、管理并提供全局访问点

它的核心意图是:“保证一个类只有一个实例,并提供一个全局访问点。

但,听起来简单,写起来容易,真正用得好却很难。为什么?因为单例涉及到:

  • 线程安全
  • 延迟加载
  • 生命周期管理
  • 依赖注入体系的冲突
  • 测试难度

二、单例的实现方式

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>();

你获得了单例的效果,但避免了“硬编码 + 不可测试”的老路子。


三、单例适用场景(别乱用,不然日后有你哭的)

单例是“全局状态”的一种表现。以下情况可以考虑:

适用场景:

  1. 配置类(Configuration/Settings)
  2. 日志服务(Logger)
  3. 线程池、连接池(ConnectionPool)
  4. 缓存管理器(CacheProvider)
  5. 全局管理器类(例如窗口管理器、事件调度器)

反模式警告:滥用单例

如果你用单例来绕过依赖注入、降低模块耦合,恭喜你……未来你将收获测试地狱、代码僵化、隐藏依赖这三大苦果。


四、总结

单例,是一种对“控制力”的渴望。你不想让对象在系统中泛滥,所以你关起门来,只允许一个版本存在。它的初衷是限制,不是滥用;是约束,不是随意。