You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将运行时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会先于自定义转换器执行,且不支持调用其他自定义转换器构建对象。
  • 修改接口以接受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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 11:15:43