一、实时修改配置 → 不适合固定单例,分析和改造方案
核心:单例适合「程序生命周期内,策略固定不变」;如果你支持运行时改配置、切换存储模式,全局固定单例就不合适了。
两种场景区分
-
配置只读,启动加载一次(之前的场景)
程序启动读一次配置,全程不变 → 单例完全没问题,只创建一次实例。 -
配置可以实时修改(你现在的场景)
运行时,用户改json配置,点击刷新,程序要切换到另一种存储实现(XData ↔ XRecord ↔ BlockAttr)。
- 如果用固定单例:实例是第一次创建好锁死的,就算配置改了,还是旧实例,无法切换。
- ❌ 不能简单加个
_instance=null然后重新赋值:
多线程/多业务同时读写CAD实体时,正在使用旧store,突然替换实例,会出现一半业务用旧实现、一半用新实现,引发诡异bug。
推荐方案(运行时可切换策略)
方案A:工厂 + 持有「可替换的实例引用」(推荐,最简单)
不要把实例做成静态只读单例,而是用一个静态引用变量保存当前有效的store实例。
public static class CadPropertyStoreFactory
{
// 这里不是单例,只是保存当前在用的实例引用
private static ICadPropertyStore _currentStore;
// 程序启动 / 用户刷新配置时调用一次
public static void ReloadStore(string configMode)
{
_currentStore = configMode switch
{
"BlockAttr" => new BlockAttributeStore(),
"XData" => new XDataStore(),
"XRecord" => new XRecordStore(),
_ => throw new NotSupportedException($"不支持存储模式:{configMode}")
};
}
// 业务代码获取当前实例
public static ICadPropertyStore GetCurrentStore()
{
if (_currentStore == null)
throw new InvalidOperationException("请先加载配置初始化Store");
return _currentStore;
}
}
业务调用:
// 用户修改配置后,执行一次刷新
CadPropertyStoreFactory.ReloadStore(ReadConfig().PropertyStoreMode);
// 业务代码获取当前生效的store
var store = CadPropertyStoreFactory.GetCurrentStore();
store.Write(ent, "key", val);
注意并发问题:
如果CAD插件存在多线程后台处理管网数据,_currentStore需要加锁,或者用volatile,防止半替换。
ZWCAD插件大多是单线程CAD命令上下文,一般不用锁,但要知道这个风险。
工作逻辑:
- 刷新配置 → 新建对应实现类实例,替换引用;
- 已经拿到旧store引用的业务代码,会继续使用旧实例执行完当前操作;
- 下一次调用
GetCurrentStore()拿到的就是新实例。
这是一种安全平滑切换,不会中途打断正在执行的读写。
方案B:每次Get都根据配置新建实例(不推荐)
public static ICadPropertyStore GetStore()
{
var mode = ReadConfig().PropertyStoreMode; // 每次都读配置文件
return mode switch { ... };
}
缺点:每次读写属性都读磁盘配置文件,频繁IO,性能差。
所以:配置文件不要每次都读,内存里缓存配置对象;配置修改时,才重新读一次配置并ReloadStore。
小结
✅ 运行时支持切换存储策略:放弃固定不变的单例,改用工厂维护当前策略引用。
本质区别:
- 经典单例:全局唯一实例,生命周期等于AppDomain,不可替换。
- 当前方案:策略实例可替换,但同一时刻全局只有一个策略生效。
二、接口 + 多个实现类 + 工厂类:是不是经典组合?
是的!这是面向对象里非常经典的组合:策略模式(Strategy Pattern) + 简单工厂模式(Simple Factory)。
拆开讲两者职责
- 接口 ICadPropertyStore:策略契约,定义统一行为。
- 多个实现类(XDataStore / BlockAttrStore):不同策略实现。
- 工厂类 CadPropertyStoreFactory:负责根据条件(配置),创建对应的策略实例,把实例选择逻辑集中一处。
策略模式核心:把可变的算法/存储逻辑封装成独立类,实现同一个接口;上层代码依赖接口,不依赖具体实现。
工厂在这里是配套工具,用来隔离实例创建逻辑,不要让业务代码到处写if/switch + new。
常见变体
- 简单工厂(我们现在写的):一个静态工厂方法,switch判断创建实例,适合你这种3种、少数几种实现。足够用,项目维护成本低。
- 工厂方法模式:每个策略对应一个工厂类,适合实现类非常多、持续大量新增场景,你的CAD属性读写工具没必要上这个,会过度设计。
- 抽象工厂:用于创建一组相关对象,这个场景用不到。
一句话区分:
- 策略模式:解决“用哪个算法/逻辑”的问题(业务逻辑可变)
- 工厂模式:解决“怎么创建对象”的问题(对象创建解耦)
两者经常成对出现,是.NET插件、CAD二次开发里最常用的一套组合。
三、额外补充一个小坑(贴合你的场景)
因为你支持运行时切换策略:
不要在各个Store实现类的构造函数里写全局持久化注册逻辑(比如多次调用 RegAppName)。
可以放到静态初始化,避免反复ReloadStore的时候重复执行注册。

1688

被折叠的 条评论
为什么被折叠?



