IRONSOFTWAREHOME

双重调度 C#:何时注入依赖关系才有意义--通过 Derek Comartin 的领域驱动设计示例进行解释

DDD 中的双重分发:何时注入依赖项才有意义

Derek Comartin

8分44秒

在 C# 编程语言领域,双调度经常被误解或利用不足。 这是一种功能强大的技术,可以在涉及的两个对象之间实现多态行为,尤其是在处理跨派生类的行为时。

在他的视频"DDD 中的双重调度:何时注入依赖关系才有意义"中,Derek Comartin 解释了双重调度如何完美地融入领域驱动设计(DDD)。 我们将深入探讨他的例子,展示它如何减轻维护负担、简化现有代码,甚至模仿其他语言中常见的访问者模式。

如果您曾经处理过 C# 中单一调度的局限性,或者试图根据对象的实例和注入的策略或规则来确定行为,那么本文将有助于阐明如何有效使用双调度 C#。

为何在领域模型中注入行为是合理的

Derek 首先指出了 DDD 中的一个常见教条--你的领域模型应该是零依赖的。 但这并不总是切实可行或有用的。 在行为建模时,您可能需要注入规则、策略或战略等逻辑。 这就是双调度的作用所在:您可以将领域概念(如策略)传递到领域对象中,然后让外部处理基于方法的评估,但领域对象仍拥有最终控制权。

当您考虑到您的耦合对象:领域行为而非基础架构时,这种模式就很有意义了。

坏例子:域中的硬编码逻辑

为了演示这个问题,Derek 从一个 Shipment 类开始,该类包含硬编码的逻辑来判断延迟:

public bool IsLate(DateTime expectedDelivery)
{
    return _systemClock.Now() > expectedDelivery;
}

现有代码简单但缺乏灵活性。 它依赖于编译时的决策,并将行为与类紧密结合。 改变规则就需要改变领域模型,而测试则更加困难,因为它与时间有关。

重构:使用策略和双重调度

Derek 介绍了 IDeliveryTimingPolicy 接口:

public interface IDeliveryTimingPolicy
{
    bool IsLate(Shipment shipment);
}

然后,他创建了两个实施方案:

1.标准交付时间政策

2.缓冲交付计时策略

这些类接收 Shipment 并根据其规则返回布尔值。 以下是 BufferedDeliveryTimingPolicy 的代码片段:

public bool IsLate(Shipment shipment)
{
    return _clock.Now() > shipment.DeliveryDate.AddMinutes(30);
}

现在,我们在 Shipment 类中使用 双分派

public bool IsLate(IDeliveryTimingPolicy policy)
{
    return policy.IsLate(this);
}

这是典型的双重调度:第一次调度是在货物上调用 IsLate() 并传递策略; 第二个调度是在策略上调用 IsLate(),并将货物作为参数。 涉及两个对象,每个对象都参与决定行为--这是单一调度无法实现的。

测试和灵活性方面的优势

这种方法可以产生高度可测试和可确定的代码。 Derek 展示了使用标准策略和缓冲策略的示例,其中测试数据受控,每个对象的运行时类型决定行为。

var policy = new BufferedDeliveryTimingPolicy(TimeSpan.FromMinutes(30));
var shipment = new Shipment { DeliveryDate = DateTime.UtcNow.AddMinutes(-15) };
Assert.False(shipment.IsLate(policy)); // Because of 30-minute buffer

这展示了如何在不改变领域模型的情况下改变运行时行为。 您可以根据策略的动态性质获得多态行为,同时保持域的完整性。

双重调度与 C# 中的访问者模式

Derek 的示例类似于访问者模式,即在不改变对象结构的情况下,定义一组对对象执行的操作。 通常情况下,在访问者模式中,您会看到以下内容

public abstract class Shape
{
    public abstract void Accept(IVisitor visitor);
}

public class Circle : Shape
{
    public override void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }
}

在这里,Accept() 和 Visit() 方法在两种类型(对象和访问者)之间进行了协调,就像 Derek 使用 Shipment 和 IDeliveryTimingPolicy 一样。 即使在 C# 这样的单一调度语言中,该模式也有助于实现多重调度行为。

使用策略集合组成规则

在下面的另一个示例中,Derek 演示了如何通过集合评估多个规则。 他介绍了以下规则

  • HasValidDestinationRule

  • 所有软件包打包规则

  • 尚未发货规则

每种实现 IShipmentReadinessRule 的方法都有:

public bool IsSatisfiedBy(Shipment shipment)

然后,"装运 "类将对它们进行这样的评估:

public bool CanShip(IEnumerable<IShipmentReadinessRule> rules)
{
    return rules.All(rule => rule.IsSatisfiedBy(this));
}

这种 void accept 模式允许您动态评估多个域规则。 如果您在配置中存储规则(尤其是在多租户应用程序中),您可以在运行时创建一个新列表并将其传递给装运。

多租户应用程序中的动态规则

Derek 指出,在多租户应用程序中,规则集可能会因客户而异。 您可以从存储中获取策略列表并动态注入:

var rules = LoadRulesForCustomer(customerId);
var canShip = shipment.CanShip(rules);

这说明了如何将动态调度和运行时决策分层到您的应用程序中。 所需的行为通过更改配置而不是模型来实现。

澄清误解:并非所有依赖关系都是坏的

最后,德里克谈到了 "在域名中注入任何东西都是不好的 "这一误解。 他强调,重要的是您要注入什么。 注入领域行为(如规范或策略)与注入基础架构不同。

领域模型仍然是切入点。 它拥有决定权,但可以将其委托给其他对象--只要它们在同一领域上下文中。

总结:双重调度 C# 在 DDD 中为何强大

最后,Derek 敦促我们进行批判性思考:不要害怕 void visit、public virtual void accept 或类似模式,只要它们能带来清晰度和可维护性。 当您以可控的方式注入业务逻辑时,您将获得灵活性和精确性。

因此,无论您是在开发一个新的类,还是在重构现有的代码库,双调度 C# 都能为您提供一种简洁的方法来分离关注点,同时保持对领域的关注。

如果您正在使用策略、规范,甚至在不知不觉中构建了访问者模式,那么您已经接近实现双重调度了。 了解了这些工具,您就能更好地控制代码在运行时的表现,提高可测试性和适应性。

结论

总而言之:C# 中的双分派是一种优雅而实用的解决方案,可以注入领域逻辑、保持封装并支持灵活的行为。 当与访问者、多重调度和动态关键字使用(仔细)等模式一起使用时,它可以编写出表现力强、健壮的领域模型。

下一次,当您调用 shipment.IsLate(policy) 时,请知道您正在利用一种基本模式,它使 C# 更接近真正的多态设计。

示例提示:如果您正在创建一个 PurchaseOrder 类,并希望根据策略确定是否可以添加一个 Item,请尝试将策略传递给 PurchaseOrder,然后让策略访问订单。 这就是双重派遣的实际效果。

请在他的 YouTube 频道上观看完整视频,了解有关该主题的更多信息。

Earn More by Sharing What You Love

Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Let's Stay in Touch!

Join our newsletter, you’ll get exclusive access on article updates. We value your privacy

Key in blue circle

立即获取免费的 30 天试用版密钥

Your trial license will be sent to your email address

无任何限制。100% 解锁。无需信用卡。

OR
bullet_checked无需信用卡或创建账户无任何限制。100% 解锁。无需信用卡。
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried Iron Suite
获取您的无义务咨询
填写下面的表格或通过sales@ironsoftware.com
您的资料将始终保密。
深受全球数百万工程师信赖
Iron Software 的客户徽标
立即获取您的免费30 天试用密钥
无需信用卡或创建账户