如何将运行时MyBar传入AddBarTransformation?设计模式咨询
问题背景
我定义了如下数据类:
public Data { Foo MyFoo Bar MyBar }
Foo需要大量修改清理才能使用,因此定义了接口:
public IFooTransformation { Foo Transform(Foo foo) }
用户创建的IFooTransformation列表存储于JSON文件,运行时反序列化加载:
var transformations = JsonSerializer.Deserialize<IFooTransformation>(jsonText); foreach (var transformation in transformations) { foo = transformation.Transform(foo); }
此前运行正常,但现在需要创建AddBarTransformation。其他转换所需属性均已包含在JSON中,但MyBar仅在运行时可用。请问有什么设计模式能帮助将MyBar传入AddBarTransformation?
我目前的思路:
- 使用自定义JsonConverter为
AddBarTransformation设置Bar为MyBar- 可能可行但不够优雅,且尚未实现;另外因反序列化接口使用了Json.Abstractions NuGet包,其
JsonAbstractionConverter会先于自定义转换器执行,且不支持调用其他自定义转换器构建对象。
- 可能可行但不够优雅,且尚未实现;另外因反序列化接口使用了Json.Abstractions NuGet包,其
- 修改接口以接受
Data而非Foo- 其他转换仅需Foo,此方案不合理
- 通过反射遍历转换实例,为需要Bar的实例赋值
- 将
IFooTransformation的逻辑与数据分离,使用Builder或Factory创建实例并传入Bar- 相比方案2过于复杂
推荐解决方案
1. 拆分接口+类型判断(最简洁实用)
新增一个标记接口,专门标识需要依赖Bar的转换:
public interface IBarDependentFooTransformation : IFooTransformation { Bar MyBar { get; set; } }
让AddBarTransformation实现这个接口:
public class AddBarTransformation : IBarDependentFooTransformation { public Bar MyBar { get; set; } public Foo Transform(Foo foo) { // 这里写使用MyBar处理Foo的逻辑 return foo; } }
反序列化完成后,遍历转换列表,为实现了该接口的实例注入运行时获取的MyBar:
var transformations = JsonSerializer.Deserialize<List<IFooTransformation>>(jsonText); var runtimeMyBar = GetMyBarAtRuntime(); // 替换成你获取运行时MyBar的逻辑 foreach (var transformation in transformations) { // 直接判断类型,比反射更直观 if (transformation is IBarDependentFooTransformation barDepTransform) { barDepTransform.MyBar = runtimeMyBar; } foo = transformation.Transform(foo); }
这个方案完全不影响原有转换的逻辑,侵入性极低,代码可读性强,实现成本也最低。
2. 上下文模式(高扩展性)
如果后续可能需要注入更多运行时数据,可以定义一个转换上下文类:
public class FooTransformationContext { public Bar MyBar { get; set; } // 未来可以在这里加其他需要的运行时属性,比如UserInfo、Config等 }
然后扩展原有接口,新增带上下文的转换方法:
// 保留原有接口,兼容所有旧转换 public interface IFooTransformation { Foo Transform(Foo foo); } // 新增带上下文的接口,供需要运行时依赖的转换实现 public interface IFooTransformationWithContext : IFooTransformation { Foo Transform(Foo foo, FooTransformationContext context); }
让AddBarTransformation实现新接口:
public class AddBarTransformation : IFooTransformationWithContext { // 旧方法直接抛异常,避免被误调用 public Foo Transform(Foo foo) { throw new InvalidOperationException("请调用带上下文的Transform方法"); } public Foo Transform(Foo foo, FooTransformationContext context) { // 使用context.MyBar处理Foo return foo; } }
运行时调用时,先创建上下文,再区分处理不同类型的转换:
var transformations = JsonSerializer.Deserialize<List<IFooTransformation>>(jsonText); var context = new FooTransformationContext { MyBar = GetMyBarAtRuntime() }; foreach (var transformation in transformations) { if (transformation is IFooTransformationWithContext ctxTransform) { foo = ctxTransform.Transform(foo, context); } else { foo = transformation.Transform(foo); } }
这个方案扩展性极强,适合未来有更多运行时依赖的场景,同时完全兼容原有代码。
对现有思路的点评
- 方案1(自定义Converter):受限于Json.Abstractions的转换器优先级问题,实现难度高且代码不够直观,不推荐。
- 方案2(修改接口传Data):强制所有转换接收不需要的
Data对象,违背单一职责原则,侵入性太强,不合理。 - 方案3(反射赋值):比类型判断的方式更隐晦,后期维护成本高,不如直接判断接口清晰。
- 方案4(Builder/Factory):为单个依赖引入复杂的创建逻辑,过度设计,没必要。
内容的提问来源于stack exchange,提问作者Brian Loftus
相关产品推荐
相关产品推荐

