一、设计模式与单例模式基础
1. 设计模式的本质
设计模式是解决软件开发特定问题的成熟解决方案 ,核心目标是通过规范化的设计思路,实现代码的高内聚 (模块专注单一功能)和低耦合(减少模块间依赖),从而提升代码的可重用性、可维护性和灵活性。
2. 高内聚与低耦合
-
高内聚:一个模块(类 / 组件)应专注于完成单一功能,类似公司中 "部门各司其职"(如财务部只负责财务,人事部只负责人事),避免功能混杂。
-
低耦合:模块间的依赖应尽可能少,交互通过明确接口进行,类似公司部门间通过负责人对接,而非直接跨部门干预,确保模块可独立移植或修改。
代码示例:
cs
// 1. 饿汉式单例(线程安全,但可能提前初始化)
public sealed class EagerSingleton
{
// 静态构造函数会在第一次使用类时调用
private static readonly EagerSingleton instance = new EagerSingleton();
// 私有构造函数阻止外部实例化
private EagerSingleton() { }
// 提供全局访问点
public static EagerSingleton Instance
{
get { return instance; }
}
// 单例类的其他方法
public void DoSomething()
{
Console.WriteLine("EagerSingleton is doing something.");
}
}
// 2. 懒汉式单例(双重锁定检查,线程安全且延迟初始化)
public sealed class LazySingleton
{
// 声明为volatile确保多线程环境下的可见性
private static volatile LazySingleton instance;
private static readonly object syncRoot = new object();
private LazySingleton() { }
public static LazySingleton Instance
{
get
{
// 第一次检查,避免每次都加锁
if (instance == null)
{
// 加锁确保线程安全
lock (syncRoot)
{
// 第二次检查,防止多个线程同时通过第一次检查
if (instance == null)
{
instance = new LazySingleton();
}
}
}
return instance;
}
}
public void DoSomething()
{
Console.WriteLine("LazySingleton is doing something.");
}
}
// 3. 使用Lazy<T>实现(.NET 4.0及以上推荐方式)
public sealed class ModernSingleton
{
// 使用Lazy<T>自动实现延迟初始化和线程安全
private static readonly Lazy<ModernSingleton> lazyInstance =
new Lazy<ModernSingleton>(() => new ModernSingleton());
public static ModernSingleton Instance
{
get { return lazyInstance.Value; }
}
private ModernSingleton() { }
public void DoSomething()
{
Console.WriteLine("ModernSingleton is doing something.");
}
}
// 4. 静态内部类实现(线程安全且延迟初始化)
public sealed class NestedSingleton
{
// 私有构造函数
private NestedSingleton() { }
// 静态内部类
private static class SingletonHolder
{
// 当第一次访问NestedSingleton.Instance时才会初始化
internal static readonly NestedSingleton instance = new NestedSingleton();
}
public static NestedSingleton Instance
{
get { return SingletonHolder.instance; }
}
public void DoSomething()
{
Console.WriteLine("NestedSingleton is doing something.");
}
}
// 使用示例
public class SingletonDemo
{
public static void Main()
{
// 使用饿汉式单例
EagerSingleton eager = EagerSingleton.Instance;
eager.DoSomething();
// 使用懒汉式单例
LazySingleton lazy = LazySingleton.Instance;
lazy.DoSomething();
// 使用现代Lazy<T>单例
ModernSingleton modern = ModernSingleton.Instance;
modern.DoSomething();
// 使用静态内部类单例
NestedSingleton nested = NestedSingleton.Instance;
nested.DoSomething();
// 验证单例唯一性
EagerSingleton eager2 = EagerSingleton.Instance;
Console.WriteLine($"Eager instances are the same: {eager == eager2}");
}
}
这些单例模式实现各有特点:
-
饿汉式:实现简单,线程安全,但会在类加载时就初始化实例,可能造成资源浪费
-
懒汉式(双重锁定):实现了延迟初始化,通过双重检查锁定保证线程安全,在多线程环境下性能较好
-
Lazy<T>实现:这是.NET 4.0 及以上版本推荐的方式,利用框架内置的 Lazy<T>类型,既保证线程安全又实现延迟初始化,代码简洁
-
静态内部类:巧妙利用 C# 的类加载机制实现延迟初始化和线程安全,性能优秀
在实际开发中,推荐使用Lazy<T>
方式实现单例,它是框架提供的线程安全实现,代码简洁且不易出错。所有这些实现都通过私有构造函数确保类不能被外部实例化,并提供了一个全局访问点Instance
属性。
二、单例模式的核心定义与目标
1. 官方定义
确保一个类只有一个实例,并提供一个全局访问点。
-
核心要素:
-
唯一性:无论何时何地访问,该类只能创建一个实例(避免重复创建导致的资源冲突或浪费)。
-
全局访问:提供统一的入口获取该实例,方便系统中任何地方调用。
-
2. 为何需要单例模式?
当系统中某个对象的多实例会导致问题时,单例模式是最优解。典型场景包括:
-
资源独占场景:如操作系统的 "任务管理器"(只能打开一个窗口,避免多实例抢占系统资源)。
-
状态一致性场景:如配置管理器(全局配置需统一,多实例可能导致配置不一致)。
-
高频访问且资源密集型对象:如数据库连接池(重复创建连接会消耗大量资源,单例可复用连接)。
三、单例模式与静态类的核心区别
单例模式(实例对象)与静态类(静态方法集合)常被混淆,但二者在设计目的和特性上有本质区别,具体对比如下:
对比维度 | 单例模式 | 静态类 |
---|---|---|
本质 | 一个全局唯一的对象(可实例化,存在对象状态) | 一组静态方法的集合(不可实例化,无对象状态) |
继承与多态 | 可继承父类,方法可被override (支持面向对象特性) |
不可继承,静态方法无法被override (不支持面向对象特性) |
初始化时机 | 支持懒加载(首次使用时才创建实例,节省资源) | 编译期初始化(程序启动时即加载,无法延迟) |
状态维护 | 可维护对象状态(如内部字段值的变更) | 无状态(仅通过静态字段维护数据,易引发线程安全问题) |
性能 | 方法调用为运行时绑定(略慢) | 方法调用为编译期绑定(更快) |
适用场景 | 需维护状态、需面向对象特性(继承 / 多态)的场景 | 仅提供工具方法(无状态操作),如数学计算、字符串工具类 |
四、单例模式的实现核心原则
(即使未提供代码,实现单例模式需遵循以下原则以保证 "唯一性"):
-
私有构造函数 :禁止外部通过
new
创建实例(核心约束)。 -
静态私有实例:类内部维护唯一实例(确保全局唯一)。
-
公共静态方法 :提供全局访问点(如
GetInstance()
),返回唯一实例。
五、总结:单例模式的核心价值
单例模式是 "唯一性" 与 "灵活性" 的平衡方案:
-
解决了 "全局唯一实例" 的问题(避免多实例冲突);
-
保留了面向对象的特性(继承、多态、状态维护),比静态类更灵活;
-
适用于需全局访问且需唯一实例的场景(如资源管理器、配置中心等)。
理解单例模式的关键在于:何时需要 "唯一实例",以及为何静态类无法满足这类场景的面向对象需求。