IRONSOFTWAREHOME

雙重分派C#:何時注入依賴是有意義的 — 通過Derek Comartin的領域驅動設計範例解析

Double Dispatch in DDD: When Injecting Dependencies Makes Sense

Derek Comartin

8m 44s

在C#程式語言領域中,雙重調度經常被誤解或未充分利用。 這是一種強大的技術,能夠在涉及的兩個物件之間實現多態行為,特別是在處理派生類的行為時。

在他的视频"Double Dispatch in DDD: When Injecting Dependencies Makes Sense"中,Derek Comartin 解釋了雙重調度如何完美地融入領域驅動設計(DDD)中。 我們將深入探索他的例子,展示如何減少維護負擔、簡化現有程式碼,甚至模仿其他語言中常見的訪客模式。

如果您曾經面對C#中單一調度的限制,或試圖根據物件實例和引入的策略或規則來決定行為,這篇文章將幫助您釐清如何有效地使用C#的雙重調度。

為什麼在領域模型中引入行為是合理的

Derek首先指出在DDD中一個常見的教條——您的領域模型應該完全沒有依賴。 但這並不總是實用或有用的。 在建模行為時,您可能需要引入邏輯,如規則、政策或策略。 這就是雙重調度的用武之地:您可以將領域概念(如政策)傳遞到領域物件中,並讓基於方法的評估在外部處理——但領域物件仍然具有最終控制權。

當您考慮您所攜帶的東西時,此模式是合理的:領域行為,而不是基礎設施。

糟糕的例子:領域中的硬編程邏輯

為了展示問題,Derek以一個包含硬編程邏輯以確定晚到的運輸類開始:

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

這現有的程式碼簡單但不靈活。 它依賴於編譯時的決定,並且將行為緊密結合到類中。 更改規則需要更改領域模型,並且測試更加困難,因為它取決於時間。

重構:使用政策和雙重調度

Derek引入介面IDeliveryTimingPolicy:

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

然後他建立了兩個實現:

  1. StandardDeliveryTimingPolicy

  2. BufferedDeliveryTimingPolicy

這些類接受運輸並根據其規則返回布林值。 以下是來自BufferedDeliveryTimingPolicy的程式碼片段:

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

現在在運輸類中,我們使用雙重調度

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使用的運輸和IDeliveryTimingPolicy一樣。 這種模式甚至在像C#這樣的單一調度語言中,也有助於實現多重調度行為。

通過政策集合組成規則

在接下來的另一個例子中,Derek展示了如何通過集合評估多個規則。 他引入了如下規則:

  • HasValidDestinationRule

  • AllPackagesPackedRule

  • NotAlreadyShippedRule

每個都實現了IShipmentReadinessRule:

public bool IsSatisfiedBy(Shipment shipment)

然後,運輸類這樣評估它們:

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

這種可接受的空模式允許動態評估多個領域規則。 如果您將規則儲存在配置中(尤其是在多租戶應用中),可以在運行時建立一個新列表並將其傳遞給運輸。

多租戶應用中的動態規則

Derek指出,在多租戶應用中,規則集可能會因為客戶而改變。 您可以從儲存中獲取政策列表並動態注入它們:

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

這展示了如何將動態調度和運行時決策層次化到應用中。 所需行為是通過改變配置,而不是模型來實現的。

澄清誤解:並非所有依賴都是壞的

最後,Derek闡述了一個誤解,即將任何事物注入領域都是壞的。 他強調的是注入了什麼才是重要的。 注入領域行為,如規範或政策,與注入基礎設施不同。

領域模型仍然是入口點。 它擁有決策權,但可以將其委派給其他物件,只要它們在同一個領域上下文中。

總結:為什麼在DDD中雙重調度C#非常強大

Derek最後敦促我們要批判性地思考:當它們帶來清晰度和可維護性時,不要害怕使用空訪客、公用虛擬空接收或類似模式。 當您以一種受控的方式注入業務邏輯時,您可以獲得靈活性和精確性。

因此,無論您是在製作一個新的類還是重構現有的程式碼庫,雙重調度C#為您提供了一種清晰的方法來分離關注點,同時保持對領域的專注。

如果您正在使用政策、規範,甚至毫無察覺地構建訪客模式,那麼您已經接近於實現雙重調度了。 理解它讓您更好地掌控程式碼在運行時的行為,提高可測試性和適應性。

結論

總之:C#中的雙重調度可以是一個優雅且實用的解決方案,用於注入領域邏輯、保持封裝性和支持靈活的行為。 當與訪客、多重調度、動態關鍵字使用(謹慎)等模式結合使用時,能夠撰寫表達力強大、健全的領域模型。

所以,下次您調用shipment.IsLate(policy)時,請意識到您正在利用一種基本的模式,將C#推向真正的多態設計。

範例提示:如果您正在建立一個PurchaseOrder類,並希望根據政策確定能否新增一個項目,嘗試將政策傳遞給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% 解鎖。無需信用卡。

bullet_checked無需信用卡或建立帳號無任何限制。100% 解鎖。無需信用卡。
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
獲取您的無義務諮詢
填寫以下表格或發送電子郵件至sales@ironsoftware.com
您的詳細資訊將始終保密。
被全球數百萬工程師信任
Iron Software的客戶標誌
立即獲取您的30天試用金鑰
無需信用卡或帳戶建立