IRONSOFTWAREHOME

Podwójne Dispatch C#: Kiedy wstrzykiwanie zależności ma sens – wyjaśnione na przykładzie podejścia Domain-Driven Design Dereka Comartina

Podwójne wywołanie w DDD: Kiedy wstrzyknięcie zależności ma sens

Derek Comartin

8m 44s

W zakresie języka programowania C#, podwójne wysyłanie jest często źle rozumiane lub niedostatecznie wykorzystywane. To potężna technika, która umożliwia zachowanie polimorficzne pomiędzy dwoma zaangażowanymi obiektami, zwłaszcza podczas obsługi zachowania w klasach pochodnych.

W swoim wideo "Double Dispatch in DDD: When Injecting Dependencies Makes Sense", Derek Comartin wyjaśnia, jak podwójne wysyłanie idealnie pasuje do Domain-Driven Design (DDD). Zgłębimy jego przykłady, pokazując, jak może zmniejszyć obciążenie konserwacyjne, uprościć istniejący kod, a nawet naśladować wzorce takie jak wzorzec gościa, często spotykane w innych językach.

Jeśli kiedykolwiek miałeś do czynienia z ograniczeniami pojedynczego wysyłania w C#, lub starałeś się ustalić zachowanie na podstawie zarówno instancji obiektu, jak i wstrzykniętej strategii lub reguły, ten artykuł pomoże wyjaśnić, jak skutecznie można używać podwójnego wysyłania w C#.

Dlaczego wstrzykiwanie zachowania ma sens w modelach domenowych

Derek zaczyna od wskazania powszechnego dogmatu w DDD — że wasz model domenowy powinien być wolny od zależności. Ale to nie zawsze jest praktyczne lub użyteczne. Podczas modelowania zachowania, możemy potrzebować wstrzykiwać logikę, taką jak reguły, polityki lub strategie. To właśnie tutaj podwójne wysyłanie pomaga: można przekazać koncepcję domeny, np. politykę, do obiektu domeny i pozwolić, by ocena metod była obsługiwana zewnętrznie — a mimo to obiekt domeny ma ostateczną kontrolę.

Ten wzorzec ma sens, jeśli weźmiemy pod uwagę, do czego się łączymy: do zachowania domeny, a nie do infrastruktury.

Zły przykład: zakodowana logika w domenie

Aby zademonstrować problem, Derek zaczyna od klasy Shipment zawierającej zakodowaną logikę do określenia opóźnienia:

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

Ten istniejący kod jest prosty, ale niewielce elastyczny. Polega na decyzjach w czasie kompilacji i ściśle łączy zachowanie z klasą. Zmiana reguły wymagałaby zmiany modelu domeny, a testowanie jest trudniejsze, ponieważ jest zależne od czasu.

Refaktoryzacja: używanie polityk i podwójnego wysyłania

Derek wprowadza interfejs IDeliveryTimingPolicy:

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

Następnie tworzy dwie implementacje:

  1. StandardowaPolitykaCzasuDostawy

  2. BuforowanaPolitykaCzasuDostawy

Te klasy przyjmują Shipment i zwracają wartość logiczną na podstawie swojej reguły. Oto fragment kodu z BufferedDeliveryTimingPolicy:

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

Teraz w klasie Shipment używamy podwójnego wysyłania:

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

To klasyczne podwójne wysyłanie: pierwsze wysyłanie polega na wywołaniu IsLate() na przesyłce i przekazaniu polityki; drugie wysyłanie polega na wywołaniu IsLate() na polityce z przesyłką jako parametrem. Dwa obiekty są zaangażowane, każdy uczestniczy w określaniu zachowania - coś, czego pojedyncze wysyłanie nie jest w stanie osiągnąć.

Korzyści z testowania i elastyczność

To podejście prowadzi do bardzo testowalnego i deterministycznego kodu. Derek pokazuje przykłady, używając standardowych i buforowanych polityk, gdzie dane testowe są kontrolowane, a typ w czasie wykonania każdego obiektu decyduje o zachowaniu.

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

Pokazuje to, jak można zmienić zachowanie w czasie wykonania bez zmiany modelu domeny. Uzyskujesz polimorficzne zachowanie na podstawie dynamicznej natury polityki, jednocześnie utrzymując integralność domeny.

Podwojne Wysyłanie a Wzor Odwiedzającego w C#

Przykład Dereka przypomina wzorzec wizytatora, gdzie definiujesz zestaw operacji do przeprowadzenia na obiektach bez zmieniania ich struktury. Typowo, w wzorcu wizytatora, zobaczyłbyś coś takiego:

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

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

Tutaj, metody Accept() i Visit() są skoordynowane pomiędzy dwoma typami — obiektem i wizytatorem — podobnie jak użycie Shipment i IDeliveryTimingPolicy przez Dereka. Ten wzorzec pomaga zaimplementować zachowanie wielokrotnego wysyłania nawet w języku z pojedynczym wysyłaniem, takim jak C#.

Kompozycja reguł z kolekcjami polityk

W kolejnym przykładzie Derek pokazuje, jak można oceniać wiele reguł za pomocą kolekcji. Wprowadza reguły takie jak:

  • PosiadaWażnąZasadęDocelową

  • WszystkiePakietySpakowaneZasada

  • ZasadaNieJestJeszczeWysłana

Każda implementuje IShipmentReadinessRule przy użyciu:

public bool IsSatisfiedBy(Shipment shipment)

Następnie klasa Shipment ocenia je w ten sposób:

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

Ten wzorzec akceptacji pozwala ocenić dynamicznie wiele reguł domenowych. Jeśli przechowujesz reguły w konfiguracji (szczególnie w aplikacji dla wielu najemców), możesz stworzyć nową listę w czasie wykonania i przekazać ją przesyłce.

Dynamiczne reguły w aplikacjach multi-tenant

Derek zauważa, że w aplikacjach multi-tenant, zestaw reguł może się zmieniać dla każdego klienta. Możesz pobrać listę polityk z magazynu i wstrzyknąć je dynamicznie:

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

Pokazuje to, jak dynamiczne wysyłanie i podejmowanie decyzji w czasie wykonania można wdrożyć w aplikacji. Pozadane zachowanie jest osiągane przez zmiane konfiguracji, a nie modelu.

Wyjaśniając nieporozumienia: nie wszystkie zależności są złe

Pod koniec Derek porusza błędne przekonanie, że wstrzykiwanie czegokolwiek do domeny jest złe. Podkreśla, że ważne jest co wstrzykujesz. Wstrzykiwanie zachowania domeny, jak specyfikacje lub polityki, nie jest tym samym co wstrzykiwanie infrastruktury.

Model domeny pozostaje punktem wejścia. Ma kontrolę nad decyzją, ale może ją delegować na inne obiekty — pod warunkiem, że są one w tym samym kontekście domenowym.

Podsumowanie: Dlaczego Podwojne Wysylanie C# jest Potężne w DDD

Derek kończy, zachęcając nas do krytycznego myślenia: nie bójcie się void visit, public virtual void accept ani podobnych wzorców, gdy przynoszą one klarowność i łatwość w utrzymaniu. Kiedy wstrzykujesz logikę biznesową w kontrolowany sposób, zyskujesz elastyczność i precyzję.

Niezależnie od tego, czy pracujesz nad nową klasą, czy refaktoryzujesz istniejącą bazę kodu, podwójne wysyłanie w C# daje czysty sposób na oddzielenie odpowiedzialności, z jednoczesnym zachowaniem skupienia na domenie.

Jeśli używasz polityk, specyfikacji, czy nawet budujesz wzorzec wizytatora, nawet tego nie zauważywszy, jesteś już blisko implementacji podwójnego wysyłania. Zrozumienie tego daje większą kontrolę nad tym, jak kod zachowuje się w czasie wykonywania, poprawiając testowalność i zdolność adaptacji.

Wnioski

Podsumowując: podwójne wysyłanie w C# może być eleganckim i praktycznym rozwiązaniem do wtryskiwania logiki domenowej, zachowania kapsułkowania i wspierania elastycznych zachowań. Gdy używane z wzorcami takimi jak wizytator, wielokrotne wysyłanie i użycie dynamicznego słowa kluczowego (z ostrożnością), umożliwia pisanie wyrazistych, potężnych modeli domenowych.

Więc następnym razem, gdy wywołasz shipment.IsLate(policy) - wiedz, że wykorzystujesz fundamentalny wzorzec, który przybliża C# do prawdziwie polimorficznego projektowania.

Porada: Jeśli tworzysz klasę PurchaseOrder i chcesz określić, czy można dodać element na podstawie polityki, spróbuj przekazać politykę do PurchaseOrder — i niech polityka odwiedzi zamówienie. To podwójne wysyłanie w działaniu.

Obejrzyj pełne wideo na jego kanale YouTube Channel i zdobywaj więcej informacji na ten temat.

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

Uzyskaj natychmiast swój darmowy 30-dniowy Klucz Testowy.

Your trial license will be sent to your email address

Brak ograniczeń. 100% dostępności. Bez karty kredytowej.

bullet_checkedNie wymaga karty kredytowej ani tworzenia kontaBrak ograniczeń. 100% dostępności. Bez karty kredytowej.
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
Otrzymaj swoją Konsultację Bez Zobowiązań
Wypełnij poniższy formularz lub wyślij e-mail na sales@ironsoftware.com
Twoje dane zawsze będą utrzymywane w tajemnicy.
Zaufane przez miliony inżynierów na całym świecie
Logotypy klientów Iron Software
Otrzymaj swój darmowy Klucz Próbny na 30 dni natychmiast.
Nie wymaga karty kredytowej ani tworzenia konta