Double Dispatch C#: Bağımlılıkları enjekte etmenin anlamlı olduğu örnekler – Derek Comartin'in Domain-Driven Design Örneği ile Açıklanmıştır
C# programlama dili alanında, çift gönderim genellikle yanlış anlaşılır veya yetersiz kullanılır. Bu, türetilmiş sınıflar arasında davranışı işlerken, özellikle iki obje arasında çok biçimli davranışı etkinleştiren güçlü bir tekniktir.
Derek Comartin, "DDD'de Çift Dağıtım: Bağımlılıkları Enjeksiyon Mantıklı Olduğunda" başlıklı videosunda, çift gönderimin Alan Yönelimli Tasarım (DDD) içine nasıl mükemmel bir şekilde uyduğunu anlatıyor. Onun örneklerini derinlemesine inceleyeceğiz ve bakım yükünü nasıl azaltabileceğini, mevcut kodu nasıl basitleştirebileceğini ve genellikle diğer dillerde görülen ziyaretçi deseni gibi desenleri nasıl taklit edebileceğini göstereceğiz.
Daha önce C#'ta tek gönderimin sınırlamalarıyla karşılaştıysanız ya da hem nesneye hem de enjekte edilen bir strateji veya kurala dayalı davranış belirlemeye çalıştıysanız, bu makale çift gönderimin C#'ta nasıl etkili biçimde kullanılabileceğini açıklıkla ortaya koyacaktır.
Domen Modellerinde Davranış Enjekte Etmenin Mantıklı Olduğu Durumlar
Derek, DDD'deki yaygın bir dogmanın, domen modelinizin sıfır bağımlılığa sahip olması gerektiği fikriyle başladığını belirtiyor. Ancak her zaman pratik veya yararlı değildir. Davranışı modellediğinizde, kurallar, politikalar veya stratejiler gibi mantığı enjekte etmeniz gerekebilir. İşte bu durumda çift gönderim devreye girer: bir politika gibi bir domen kavramını, domen objesine iletebilir ve metod tabanlı değerlendirmeyi dışsal olarak ele alabilirsiniz, ancak yine de domen objesi son kontrolü elinde tutar.
Bu desen, bağlandığınız şeyin altyapı değil, domen davranışı olduğunu düşündüğünüzde mantıklı gelir.
Kötü Örnek: Domen İçinde Kodlanmış Mantık
Sorunu göstermek için, Derek, gecikmeyi belirlemek için kodlanmış mantık içeren bir Yük gönderi sınıfıyla başlar:
public bool IsLate(DateTime expectedDelivery)
{
return _systemClock.Now() > expectedDelivery;
}public bool IsLate(DateTime expectedDelivery)
{
return _systemClock.Now() > expectedDelivery;
}Mevcut kod basittir fakat esnek değildir. Çalışma zamanı kararlarına dayanır ve davranışı sınıfa sıkıca bağlar. Kuralı değiştirmek, alan modelini değiştirmeyi gerektirir ve test etmesi daha zordur çünkü zamana duyarlıdır.
Yeniden Düzenleme: Politikalar ve Çift Gönderim Kullanma
Derek, IDeliveryTimingPolicy arayüzünü tanıtır:
public interface IDeliveryTimingPolicy
{
bool IsLate(Shipment shipment);
}public interface IDeliveryTimingPolicy
{
bool IsLate(Shipment shipment);
}Daha sonra iki uygulama oluşturur:
StandartTeslimatZamanlamaPolitikasi
- TamponluTeslimatZamanlamaPolitikasi
Bu sınıflar Yük taşımasını alır ve kurallarına göre bir boolean döndürür. İşte BufferedDeliveryTimingPolicy'den bir sonraki kod parçası:
public bool IsLate(Shipment shipment)
{
return _clock.Now() > shipment.DeliveryDate.AddMinutes(30);
}public bool IsLate(Shipment shipment)
{
return _clock.Now() > shipment.DeliveryDate.AddMinutes(30);
}Şimdi Shipment sınıfında, çift dağıtımı kullanıyoruz:
public bool IsLate(IDeliveryTimingPolicy policy)
{
return policy.IsLate(this);
}public bool IsLate(IDeliveryTimingPolicy policy)
{
return policy.IsLate(this);
}Bu, klasik bir çift dağıtımdır: ilk dağıtım, gönderimin üzerinde IsLate() çağırarak ve politikayı geçerek yapılır; ikinci dağıtımda, gönderim bir parametre olarak alınarak, politika üzerinde IsLate() çağırılır. İki nesne söz konusudur, her biri davranışı belirlemede rol oynar—tek dağıtımın başaramayacağı bir şey.
Testte ve Esneklikteki Faydalar
Bu yaklaşım, yüksek teste tabi ve deterministik bir kod ile sonuçlanır. Derek, her bir nesnenin çalışma zamanı türünün davranışı belirlediği standart ve tampon politikalar kullanarak örnekler gösteriyor.
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 buffervar policy = new BufferedDeliveryTimingPolicy(TimeSpan.FromMinutes(30));
var shipment = new Shipment { DeliveryDate = DateTime.UtcNow.AddMinutes(-15) };
Assert.False(shipment.IsLate(policy)); // Because of 30-minute bufferBu, çalışma zamanı davranışının, alan modelini değiştirmeden nasıl değiştirilebileceğini göstermektedir. Politiğin dinamik doğasına bağlı olarak çok biçimliliği elde edersiniz, alan bütünlüğünü korurken.
C#'da Çift Dağıtım ve Ziyaretçi Deseni
Derek'in örneği ziyaretçi desenini andırmaktadır, burada yapıları değiştirmeden nesneler üzerinde gerçekleştirilecek bir dizi işlem tanımlarsınız. Tipik olarak, ziyaretçi deseninde şöyle bir şey görürsünüz:
public abstract class Shape
{
public abstract void Accept(IVisitor visitor);
}
public class Circle : Shape
{
public override void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
}public abstract class Shape
{
public abstract void Accept(IVisitor visitor);
}
public class Circle : Shape
{
public override void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
}Burada, Accept() ve Visit() yöntemleri iki tip arasında—nesne ve ziyaretçi—Derek'in Shipment ve IDeliveryTimingPolicy kullanımı gibi koordine edilir. Bu desen, C# gibi tek dağıtım dilinde bile çoklu dağıtım davranışını uygulamaya yardımcı olur.
Politika Koleksiyonları ile Kuralların Kompozisyonu
Takip eden başka bir örnekte, Derek bir koleksiyon üzerinden birden fazla kuralın nasıl değerlendirilebileceğini gösterir. Şu gibi kuralları tanıtır:
GecerliVarisYeriKurali
TumPaketlerPaketlenmisKurali
- ZatenGonderilmemisKurali
Her biri IShipmentReadinessRule'yi uygular:
public bool IsSatisfiedBy(Shipment shipment)public bool IsSatisfiedBy(Shipment shipment)Sonra, Shipment sınıfı bunları şöyle değerlendirir:
public bool CanShip(IEnumerable<IShipmentReadinessRule> rules)
{
return rules.All(rule => rule.IsSatisfiedBy(this));
}public bool CanShip(IEnumerable<IShipmentReadinessRule> rules)
{
return rules.All(rule => rule.IsSatisfiedBy(this));
}Bu boş kabul deseni, birden çok alan kuralını dinamik olarak değerlendirmenize olanak tanır. Kuralları yapılandırmada saklarsanız (özellikle çok kiracılı bir uygulamada), çalışma zamanında yeni bir liste oluşturabilir ve gönderime iletebilirsiniz.
Çok Kiracılı Uygulamalarda Dinamik Kurallar
Derek, çok kiracılı uygulamalarda, kurallar setinin müşteri başına değişebileceğine dikkat çeker. Depolamadan politika listesi alabilir ve dinamik olarak enjekte edebilirsiniz:
var rules = LoadRulesForCustomer(customerId);
var canShip = shipment.CanShip(rules);var rules = LoadRulesForCustomer(customerId);
var canShip = shipment.CanShip(rules);Bu, dinamik dağıtımın ve çalışma zamanı karar verme sürecinin uygulamanıza nasıl katmanlaştırılabileceğini gösteriyor. İstenilen davranış, modeli değil yapılandırmayı değiştirerek gerçekleştirilir.
Yanlış Anlamaları Açıklığa Kavuşturmak: Tüm Bağımlılıklar Kötü Değildir
Sonlara doğru Derek, bir alana herhangi bir şey enjekte etmenin kötü olduğuna dair yaygın yanlış anlama üzerinde duruyor. Önemli olanın ne enjekte ettiğiniz olduğunu vurgular. Alan davranışını, örneğin tanımlamalar veya politikaları enjekte etmek, altyapıyı enjekte etmekle aynı şey değildir.
Alan modeli giriş noktası olarak kalır. Karar onda olup aynı alan bağlamındaki diğer nesnelere delege edebilir.
Sonuç: Çift Dağıtım C# DDD'de Neden Güçlü
Derek, karara açık ve sürdürülebilir getirdiğinde boş ziyaret yapmak, kamuya açık sanal boş kabul veya benzer desenlerden korkmamak gerektiğini belirterek bitiriyor. Kontrollü bir şekilde iş mantığı enjekte ettiğinizde, esneklik ve hassasiyet kazanırsınız.
Yani, yeni bir sınıfta çalışıyor ya da mevcut bir kod tabanını yeniden yapılandırıyorsanız, çift dağıtım C#, ilgili kaygıları ayırmak için temiz bir yol sunar, alan odaklı kalırken.
Politikalar kullanıyor, özellikler veya hatta farkında olmadan bir ziyaretçi deseni oluşturuyorsanız, zaten çift dağıtım uygulamaya yakınsınız. Bunu anlamak, kodun çalışma zamanında nasıl davrandığını daha iyi kontrol etmenizi sağlar, test edilebilirliği ve uyarlamayı artırır.
Sonuç
Özetlemek gerekirse: C#'daki çift dağıtım, alan mantığını enjekte etmek, kapsüllemeyi korumak ve esnek davranışı desteklemek için zarif ve pratik bir çözüm olabilir. Ziyaretçi gibi desenler, çoklu dağıtım ve dinamik anahtar kelime kullanımı (dikkatle) ile kullanıldığında, etkileyici, sağlam alan modelleri yazmayı mümkün kılar.
Bir dahaki sefere gönderi.IsLate(politika) çağırdığınızda—C#'ı gerçekten çok biçimli bir tasarıma yaklaştıran temel bir deseni kullandığınızı bilin.
Örnek İpucu: Eğer bir PurchaseOrder sınıfı oluşturuyorsanız ve bir Öğenin politikalara dayalı eklenip eklenemeyeceğini belirlemek istiyorsanız, politikayı PurchaseOrder'a geçirmeyi deneyin—ve politikanın siparişi ziyaret etmesine izin verin. Bu, aktiviteli çift dağıtımdır.
Onun YouTube Kanalı'nda tam videoyu izleyin ve konu hakkında daha fazla içgörü kazanın.

