Dependency Injection Temelleri
Coddy'nin C# Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 46 / 70.
Bağımlılık Enjeksiyonu (Dependency Injection - DI), bir sınıfın bağımlılıklarını kendi içinde oluşturmak yerine dışarıdan aldığı bir tekniktir. Bu, kodu daha esnek, test edilebilir ve gevşek bağlı (loosely coupled) hale getirir.
DI olmadan, bir sınıf kendi bağımlılıklarını kendisi oluşturur; bu da değiştirmeyi veya test etmeyi zorlaştırır:
public class OrderService
{
private EmailSender sender = new EmailSender(); // Sıkı sıkıya bağlı
public void PlaceOrder() => sender.Send("Order placed");
}DI ile bağımlılıklar, yapıcı (constructor) aracılığıyla aktarılır:
public interface IMessageSender
{
void Send(string message);
}
public class EmailSender : IMessageSender
{
public void Send(string message) => Console.WriteLine("Email: " + message);
}
public class OrderService
{
private IMessageSender sender;
public OrderService(IMessageSender sender)
{
this.sender = sender;
}
public void PlaceOrder() => sender.Send("Order placed");
}Artık OrderService e-posta, SMS veya test için sahte bir gönderici kullanıp kullanmadığını bilmez veya umursamaz. Bağımlılık, nesne oluşturulduğunda "enjekte edilir":
IMessageSender emailSender = new EmailSender();
OrderService service = new OrderService(emailSender);
service.PlaceOrder(); // Email: Sipariş verildiBu desen, somut uygulamalar yerine soyutlamalara (arayüzlere) dayanma ilkesini takip eder. Bu, onları kullanan sınıfı değiştirmeden uygulamaların yer değiştirilmesine olanak tanır; bu da birim testi ve esnek mimarilerin sürdürülmesi için gereklidir.
Görev
KolayBağımlılık enjeksiyonunun (dependency injection) gücünü gösteren bir günlükleme (logging) sistemi oluşturalım. Günlüklerin nasıl yazılacağını sabit olarak kodlamak yerine, ayrıntıları bilmeden konsol, dosya simülasyonu veya başka herhangi bir günlükleme hedefiyle çalışabilen esnek bir Logger sınıfı oluşturacaksınız.
Kodunuzu üç dosya halinde düzenleyeceksiniz:
ILogWriter.cs:Loggingad alanında (namespace)ILogWriteradında bir arayüz (interface) tanımlayın. Bu arayüz, herhangi bir günlükleme hedefi için sözleşmeyi temsil eder ve mesajın nereye yazıldığını açıklayan bir dize (string) döndüren tek birWrite(string message)metoduna sahip olmalıdır.LogWriters.cs:Loggingad alanındaILogWriterarayüzünün iki somut uygulamasını oluşturun:ConsoleLogWriter-Writemetodu"Console: " + messagedöndürür.FileLogWriter- yapıcı metodu (constructor) aracılığıyla bir dosya adı alır veWritemetodu"File[" + filename + "]: " + messagedöndürür.
Logger.cs:Loggingad alanında, bağımlılığını yapıcı metot enjeksiyonu (constructor injection) yoluyla alan birLoggersınıfı oluşturun.Loggerşunları yapmalıdır:- Yapıcı metodu aracılığıyla bir
ILogWriterkabul etmeli ve bunu özel (private) bir alanda saklamalıdır. - Enjekte edilen yazıcıya (writer) yetki veren ve sonucu döndüren bir
Log(string message)metoduna sahip olmalıdır.
- Yapıcı metodu aracılığıyla bir
Program.cs: BiriConsoleLogWriter, diğeriFileLogWriterile olmak üzere iki farklı günlükçü (logger) oluşturarak bağımlılık enjeksiyonunu gösterin. AynıLoggersınıfının, hangi bağımlılığın enjekte edildiğine bağlı olarak nasıl farklı davrandığını gösterin.
İki girdi alacaksınız:
- Dosya günlükçüsü için dosya adı
- Günlüklenecek mesaj
Bir ConsoleLogWriter ile bir Logger oluşturun ve mesajı günlüğe kaydedin. Ardından (girdiden gelen dosya adını kullanarak) bir FileLogWriter ile başka bir Logger oluşturun ve aynı mesajı günlüğe kaydedin. Her sonucu kendi satırında yazdırın.
Örneğin, girdiler app.log ve System started ise, çıktı şöyle olmalıdır:
Console: System started
File[app.log]: System startedLogger sınıfının hiç değişmediğine dikkat edin; herhangi bir ILogWriter uygulamasıyla çalışır. Bağımlılık enjeksiyonunun özü budur: Logger bir soyutlamaya (arayüze) bağlıdır ve somut uygulama dışarıdan "enjekte edilir". Daha sonra Logger sınıfını hiç değiştirmeden kolayca bir DatabaseLogWriter veya CloudLogWriter ekleyebilirsiniz!
Kopya kağıdı
Bağımlılık Enjeksiyonu (Dependency Injection - DI), bir sınıfın bağımlılıklarını kendisi oluşturmak yerine dışarıdan aldığı, kodun daha esnek, test edilebilir ve gevşek bağlı (loosely coupled) olmasını sağlayan bir tekniktir.
DI olmadan (sıkı bağlı):
public class OrderService
{
private EmailSender sender = new EmailSender(); // Sıkı bağlı
public void PlaceOrder() => sender.Send("Order placed");
}DI ile (gevşek bağlı):
public interface IMessageSender
{
void Send(string message);
}
public class EmailSender : IMessageSender
{
public void Send(string message) => Console.WriteLine("Email: " + message);
}
public class OrderService
{
private IMessageSender sender;
public OrderService(IMessageSender sender)
{
this.sender = sender;
}
public void PlaceOrder() => sender.Send("Order placed");
}Bağımlılığı enjekte etme:
IMessageSender emailSender = new EmailSender();
OrderService service = new OrderService(emailSender);
service.PlaceOrder(); // Email: Order placedDI, somut uygulamalar yerine soyutlamalara (arayüzlere) dayanma ilkesini izleyerek, kullanan sınıfı değiştirmeden uygulamaların yer değiştirilmesine olanak tanır.
Kendin dene
using System;
using Logging;
class Program
{
public static void Main(string[] args)
{
// Girdileri oku
string filename = Console.ReadLine();
string message = Console.ReadLine();
// TODO: Bir ConsoleLogWriter ile bir Logger oluşturun ve mesajı günlüğe kaydedin
// Sonucu yazdırın
// TODO: Bir FileLogWriter (dosya adını kullanarak) ile başka bir Logger oluşturun ve aynı mesajı günlüğe kaydedin
// Sonucu yazdırın
}
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Nesne Yönelimli Programlama bölümündeki tüm dersler
1OOP Temelleri
Harici Dosyalarİsim Alanları ve YönergelerSınıf ve Nesnelere Giriş'this' Anahtar KelimesiMetotlar ve ParametrelerAlanlar ve ÖzelliklerYapıcı MetotlarNesne BaşlatıcılarÖzet - Basit Hesap Makinesi4Kalıtım
Temel Kalıtım (:) Sözdizimi'base' Anahtar KelimesiVirtual ve Override Anahtar KelimeleriSealed Sınıflar'object' Temel SınıfıÖzet - Çalışan Hiyerarşisi7İleri Düzey Özellikler
Operatör Aşırı Yüklemeİndeksleyiciler (this[])ToString() Geçersiz KılmaGenişletme MetotlarıÖzet - Özel Liste10Tasarım Kalıpları Bölüm 1
Tasarım Kalıplarına GirişThread-Safe SingletonFactory KalıbıObserver Kalıbı (Olaylar)Strategy Kalıbı2Özellikler ve Statik Üyeler
Otomatik Uygulanan ÖzelliklerSadece Okunur/Yazılır ÖzelliklerStatik Alanlar ve MetotlarStatik Sınıflarİfade Gövdeli Üyeler5Polimorfizm ve Arayüzler
Derleme vs Çalışma Zamanı PolimorfizmiArayüz vs Soyut SınıfÇoklu ArayüzlerBelirgin ArayüzlerUpcasting ve DowncastingÖzet - Şekil Hesaplayıcı8İleri Düzey OOP Kavramları
Kalıtım yerine KompozisyonGenerics (Sınıflar ve Metotlar)Delegates ve EventlerAttributes ve ReflectionIDisposable ve using İfadesiDependency Injection Temelleri11Tasarım Kalıpları Bölüm 2
Komut KalıbıAdaptör KalıbıDekoratör KalıbıŞablon Metot KalıbıDurum KalıbıKompozit Kalıbı3Sınıf Mimarisi
Örnek vs Statik Veri'readonly' ve 'const' Anahtar KelimeleriDestekleyici Alanlar (Backing Fields)Özet - Banka Hesabı Yöneticisi6Kapsülleme
Erişim BelirleyicilerKapsülleme için ÖzelliklerVeri Gizleme UygulamasıDeğişmezlik KalıplarıÖzet - Öğrenci Kayıtları