如何实现工厂创建对象的批量替换且自动更新所有代码引用?
更优实现方案汇总
方案1:代理模式实现无侵入透明替换
该方案解决了原实现需要调用.Value的侵入性问题,对外始终暴露ITheme接口,调用方完全感知不到间接层的存在。
首先实现代理类:
public class ThemeProxy : ITheme { // 内部持有实际主题实例,工厂可直接修改该值 internal ITheme InnerTheme { get; set; } public ThemeProxy(ITheme initialTheme) { InnerTheme = initialTheme; } // 接口成员全部转发到内部实例 public string TextColor => InnerTheme.TextColor; public string BgrColor => InnerTheme.BgrColor; }
改造后的工厂类:
public class ProxyThemeFactory { private readonly List<WeakReference<ThemeProxy>> _proxies = new(); public ITheme CreateTheme(bool dark) { var theme = dark ? new DarkTheme() : new LightTheme(); var proxy = new ThemeProxy(theme); _proxies.Add(new WeakReference<ThemeProxy>(proxy)); // 对外直接返回ITheme接口,调用方无需感知代理存在 return proxy; } public void ReplaceTheme(bool dark) { var newTheme = dark ? new DarkTheme() : new LightTheme(); foreach (var wr in _proxies) { if (wr.TryGetTarget(out var proxy)) { proxy.InnerTheme = newTheme; } } } // 保留原有的存活实例统计能力 public string Info { get { var sb = new StringBuilder(); foreach (var wr in _proxies) { if (wr.TryGetTarget(out var proxy)) { bool isDark = proxy.InnerTheme is DarkTheme; sb.Append(isDark ? "Dark" : "Light"); sb.AppendLine(" theme"); } } return sb.ToString(); } } }
使用示例:
var factory = new ProxyThemeFactory(); var theme = factory.CreateTheme(true); Console.WriteLine(theme.BgrColor); // 输出 dark gray factory.ReplaceTheme(false); Console.WriteLine(theme.BgrColor); // 输出 white
优势:完全兼容ITheme接口,调用方代码不需要做任何修改,没有.Value的额外编码要求,也不会出现漏写.Value的低级错误。
注意点:如果ITheme接口后续新增成员,代理类需要同步新增转发逻辑,可通过源生成器自动生成代理类避免手动维护成本。
方案2:适配DI生态的可观测主题方案
如果项目本身使用依赖注入框架,可直接复用.NET内置的IOptionsMonitor能力实现全局替换,无需自己维护弱引用列表:
- 定义主题配置类
public class ThemeOptions { public bool IsDark { get; set; } }
- 注册服务
// 注册配置监听服务 services.Configure<ThemeOptions>(opt => opt.IsDark = false); // 注册ITheme为每次获取时从最新配置生成实例 services.AddTransient<ITheme>(sp => { var opt = sp.GetRequiredService<IOptionsMonitor<ThemeOptions>>().CurrentValue; return opt.IsDark ? new DarkTheme() : new LightTheme(); });
- 全局替换主题时只需要修改配置
var optMonitor = serviceProvider.GetRequiredService<IOptionsMonitor<ThemeOptions>>(); optMonitor.CurrentValue.IsDark = true; // 后续所有获取ITheme的地方都会自动拿到新的主题实例
优势:完全复用DI框架能力,不需要自己实现工厂、弱引用追踪逻辑,适合中大型项目使用。
注意点:已经提前获取到的ITheme实例不会自动更新,适合使用方每次使用前从DI容器获取实例的场景。
方案3:通用泛型代理方案
如果需要支持多个类型的全局替换,可做通用的泛型动态代理,不需要为每个接口手写代理类,可通过Castle.Core等动态代理库实现,简化实现思路如下:
public class DynamicProxy<T> : DispatchProxy where T : class { public T Inner { get; set; } public static T Create(T initial) { var proxy = Create<T, DynamicProxy<T>>(); ((DynamicProxy<T>)proxy).Inner = initial; return proxy; } protected override object Invoke(MethodInfo targetMethod, object[] args) { return targetMethod.Invoke(Inner, args); } }
使用时工厂直接返回DynamicProxy<T>.Create(实例)即可,所有接口调用都会自动转发到内部实例,替换时修改Inner属性即可。
内容的提问来源于stack exchange,提问作者Dmitri Nesteruk
相关产品推荐
相关产品推荐

