重构:将单例模式从业务类中完全解耦
先来看看Singleton的用例图:
在一个实际的老项目中,有很多的业务类,也不知道当时如何设计的,这些业务类均被设计成了Singleton模式。
项目中这些业务类结构如下:
而每个业务类的设计均为单例,诸如:
public class SearchHelper { private object obj = new object(); private static SearchHelper search; private SearchHelper(){} public static SearchHelper Instance { get { if (search == null) { lock (obj) { if (search == null) search = new SearchHelper(); } } return search; } } public string GetSearch() { return "Path"; } }
这种设计初初一看,就不是很顺眼。
这种业务类和单例本身强结合的模式,实际应用中,还真带来了一些麻烦,比如这个SearchHelper, 我有时候需要对某些方法获取更新某实体后的值,因其单例类模式,直接就无法得到其值,只好绕弯路。
于是重构这个模块,已经是摆在面前。 当然也可以不去重构,让代码继续乱下去,乱下去,乱下去,直到死亡 :-)
经过一番考虑,首先想到的是将这些业务类和单例这个模式强结合的模式分离, 分离后让单例不再强奸这些业务类。
于是,就有了下文:
第一步:将这些业务类的单例去除,出除后,代码结构如下:
public class SearchHelper { private SearchHelper(){} public string GetPath() { return "Path"; } }
同理,去除其他所有的业务类的单例。
PathHelper:
public class PathHelper { private PathHelper(){} public string GetPath() { return "Path"; }
}
MapHelper:
public class MapHelper {
private MapHelper(){} public string GetMap() { return "Map"; } }
第二步:定义个单例专用类,定义一个泛型类,并利用反射特性使其能够动态按需创建单例
public class Singleton<T> where T : class { static Mutex mutex = new Mutex(); static T instance; private Singleton() { } public static T UniqueInstance { get { mutex.WaitOne(); // enter protected area if (instance == null) { try { instance = SingletonCreator.instance; } catch (Exception ex) { throw ex; } } mutex.ReleaseMutex(); // exit protected area return instance; } } class SingletonCreator { static SingletonCreator() { } // Private object instantiated with private constructor internal static readonly T instance = CreateNewInstance(); private static T CreateNewInstance() { //Get the current assembly object Assembly assembly = Assembly.GetExecutingAssembly(); // Retrieve assembly types Type[] types = assembly.GetTypes(); foreach (Type typeName in types) { Module typeMod = assembly.GetModule(typeName.ToString()); if (typeof(T).IsAssignableFrom(typeName) && (typeName.IsClass)) { // Create and return an instance of the appropriate model object return Activator.CreateInstance(typeName) as T; } } throw new Exception("New instance not found!"); } } }
第三步: 定义一个Dispatcher类,用以得到泛型类的单例。
1 public class Dispatcher 2 { 3 public static T BuildObject<T>() where T : class 4 { 5 return Singleton<T>.UniqueInstance; 6 } 7 }
第四步: 定义一个Facades类,用以得到想要的具体的某业务类的单例。
1 public class Facades 2 { 3 private static SearchHelper searchHelper; 4 private static MapHelper mapHelper; 5 private static PathHelper pathHelper; 6 7 public SearchHelper SearchHelper 8 { 9 get { return searchHelper; } 10 } 11 12 public MapHelper MapHelper 13 { 14 get { return mapHelper; } 15 } 16 17 public PathHelper PathHelper 18 { 19 get { return pathHelper; } 20 } 21 public Facades() 22 { 23 searchHelper = Dispatcher.BuildObject<SearchHelper>(); 24 mapHelper = Dispatcher.BuildObject<MapHelper>(); 25 pathHelper = Dispatcher.BuildObject<PathHelper>(); 26 } 27 }
好了,到此为止,已经将单例模式从业务类中完全解耦。
业务类就是业务来,单例就是单例,无需将单例强加在业务类上。重构后,其文件结构如下:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)