线程安全单例的进化之路与工厂模式的解耦艺术
1. 单例模式:唯一实例的全局访问点
核心目标:确保一个类仅有一个实例,并提供全局访问入口,常用于配置管理、线程池、日志服务等场景。
1.1 线程安全单例的五种实现范式
- 饿汉式(静态初始化):
csharp
public sealed class Singleton
{
private static readonly Singleton _instance = new Singleton();
public static Singleton Instance => _instance;
private Singleton() { } // 私有构造函数
}
- 优点:线程安全(类加载时初始化)、简单高效。
- 缺点:提前占用资源,无法延迟加载。
- 懒汉式(简单锁):
csharp
public sealed class Singleton
{
private static Singleton _instance;
private static readonly object _lock = new object();
public static Singleton Instance
{
get
{
lock (_lock)
{
return _instance ??= new Singleton();
}
}
}
private Singleton() { }
}
- 优点:延迟加载,节省启动资源。
- 缺点:每次访问需加锁,性能损耗较大。
- 双重检查锁定(DCL):
csharp
public sealed class Singleton
{
private static volatile Singleton _instance;
private static readonly object _lock = new object();
public static Singleton Instance
{
get
{
if (_instance is null)
{
lock (_lock)
{
if (_instance is null)
_instance = new Singleton();
}
}
return _instance;
}
}
private Singleton() { }
}
- 关键点:
volatile
确保内存可见性,避免指令重排序导致半初始化对象被读取。 - 适用场景:.NET Framework 4.5及以下版本,需谨慎处理内存模型。
- .NET 4+ 推荐方案:Lazy
csharp
public sealed class Singleton
{
private static readonly Lazy<Singleton> _lazy = new Lazy<Singleton>(() => new Singleton());
public static Singleton Instance => _lazy.Value;
private Singleton() { }
}
- 优点:内置线程安全、延迟加载、异常封装(如构造器抛出异常时,后续调用重试)。
- 无锁单例(静态类):
csharp
public static class Singleton
{
public static void Configure(Action<Singleton> config) { ... }
}
- 适用场景:无需状态存储的工具类,通过静态方法提供功能。
1.2 单例模式的变体与扩展
- 可关闭单例:通过
Dispose
方法释放资源,允许重新初始化。 - 多例模式:控制实例数量上限(如连接池),通过对象池复用实例。
- 依赖注入集成:在ASP.NET Core中通过
AddSingleton
注册服务,实现容器管理。
2. 工厂模式:解耦对象创建与使用逻辑
核心价值:将对象的创建逻辑封装在工厂中,使客户端代码仅依赖抽象接口,提升代码灵活性与可测试性。
2.1 简单工厂:集中化的创建逻辑
- 结构:
- 工厂类:包含静态方法,根据参数返回具体产品实例。
- 产品接口:定义抽象产品行为。
- 具体产品:实现接口的具体类。
代码示例:
csharp
public interface IProduct { void Operation(); }
public class ConcreteProductA : IProduct { public void Operation() { } }
public class ConcreteProductB : IProduct { public void Operation() { } }
public static class SimpleFactory
{
public static IProduct CreateProduct(string type) => type switch
{
"A" => new ConcreteProductA(),
"B" => new ConcreteProductB(),
_ => throw new ArgumentException("未知类型")
};
}
// 客户端调用
IProduct product = SimpleFactory.CreateProduct("A");
- 缺点:违反开闭原则(新增产品需修改工厂类),工厂类职责过重。
2.2 工厂方法模式:多态工厂的扩展性
- 结构:
- 抽象工厂:定义创建产品的接口方法。
- 具体工厂:实现抽象工厂,负责创建具体产品。
- 产品接口与具体产品:同简单工厂。
代码示例:
csharp
public interface IFactory
{
IProduct CreateProduct();
}
public class ConcreteFactoryA : IFactory
{
public IProduct CreateProduct() => new ConcreteProductA();
}
public class ConcreteFactoryB : IFactory
{
public IProduct CreateProduct() => new ConcreteProductB();
}
// 客户端调用(通过具体工厂创建)
IFactory factory = new ConcreteFactoryA();
IProduct product = factory.CreateProduct();
- 优点:符合开闭原则(新增产品只需添加具体工厂),支持动态切换创建逻辑。
- 应用场景:数据库访问层(如不同数据库的连接工厂)、插件系统。
2.3 抽象工厂模式:产品族的协同创建
- 核心:提供一系列相关或依赖对象的接口,无需指定具体类。
- 适用场景:跨平台UI组件(如Windows/MacOS的按钮、文本框)、主题皮肤切换。
代码示例:
csharp
public interface IButton { void Render(); }
public interface ITextBox { void Render(); }
public interface IGUIFactory
{
IButton CreateButton();
ITextBox CreateTextBox();
}
public class WindowsFactory : IGUIFactory
{
public IButton CreateButton() => new WindowsButton();
public ITextBox CreateTextBox() => new WindowsTextBox();
}
// 客户端通过工厂创建产品族
IGUIFactory factory = new WindowsFactory();
IButton button = factory.CreateButton();
button.Render();
3. 单例与工厂的协同应用案例
案例:全局配置管理器
- 单例角色:
ConfigurationManager
作为单例,存储全局配置。 - 工厂角色:
IConfigLoaderFactory
创建不同配置加载器(如JSON、XML)。 - 协同逻辑:
csharp
public sealed class ConfigurationManager
{
private static readonly Lazy<ConfigurationManager> _lazy = new Lazy<ConfigurationManager>(() => new ConfigurationManager());
public static ConfigurationManager Instance => _lazy.Value;
private IConfigLoader _loader;
private ConfigurationManager()
{
// 通过工厂注入具体加载器
var factory = new ConfigLoaderFactory();
_loader = factory.CreateLoader("JSON");
}
}
4. 最佳实践与避坑指南
- 单例模式:
- 优先使用
Lazy<T>
实现线程安全单例,避免手动锁管理。 - 避免在单例中持有可变状态,防止并发问题。
- 工厂模式:
- 简单工厂适用于产品类型固定的场景,工厂方法更适用于需要扩展的场景。
- 抽象工厂需谨慎设计产品族接口,避免过度复杂化。
- 依赖注入集成:
- 在ASP.NET Core中,通过
IServiceCollection
注册单例或工厂,利用DI容器管理生命周期。
5. 总结与进阶方向
- 单例模式:从基础实现到线程安全优化,再到与依赖注入框架的集成,是全局状态管理的核心工具。
- 工厂模式:通过解耦创建逻辑,提升代码的灵活性与可测试性,是设计可扩展系统的基础。
- 进阶探索:
- 单例模式的序列化问题(如
[Serializable]
属性与跨进程单例)。 - 工厂模式与构建器模式(Builder)的对比,适用于复杂对象的逐步构造。
- 结合反射与依赖注入实现动态工厂,支持插件化架构。
通过本篇深度解析,你将系统掌握单例与工厂模式在C#中的实践技巧,为设计高可维护性、可扩展性的系统奠定坚实基础。