.NET DI: Interface Kaydı ve Hangi Sınıf New'leniyor
Tekrardan selamlar. Bir önceki yazıda servislerin kaç obje ürettiğini ve ne kadar yaşadığını konuştuk. O yazıyı kapatırken bir soru bırakmıştık: container bir servis istendiğinde hangi sınıfı new'liyor?
İlk iki yazıda bu sorunun cevabı hep aynıydı, çünkü kayıtlarımızın hepsi AddSingleton<Translator>() biçimindeydi. Tek tip yazdık, hangi sınıfı kaydettiysek container onu oluşturdu. Bu yazıda kaydı ikiye ayıracağız: bir tarafa ne istendiğini, diğer tarafa ne oluşturulacağını yazacağız. Aradaki boşluk da bu yazının konusu.
Neden somut sınıf değil de interface
services.AddSingleton<Translator>() satırı container'a tek bir bilgi veriyor: birisi Translator isterse Translator oluştur. Servisi kullanan tarafın da constructor'ında Translator tipini yazması gerekiyor. Yani kaydın iki ucu da aynı sınıfa bakıyor, ortada seçim yapılacak bir şey yok.
Bu kurulum çalışıyor, ama Translator'ın ne yaptığına bakınca bir maliyeti olduğu görülüyor. Sınıfın constructor'ı messages.en.txt ve messages.tr.txt dosyalarını diskten okuyor. Yani bir Translator objesi oluşturmak, dosya sistemine gitmek demek. Uygulamayı çalıştırdığımızda bu sorun değil, dosyalar orada duruyor zaten. Sorun, bu sınıfa bağımlı olan bir kodu dosya sistemi olmadan çalıştırmak istediğimizde çıkıyor.
Bir test yazdığımızı düşünelim. Test edilmek istenen şey çeviri değil, çeviriyi kullanan akış. Ama Translator'a bağımlı olduğumuz için testin de o iki dosyayı doğru yerde bulması gerekiyor. Test, ilgilenmediği bir şeyin kurulumunu taşımak zorunda kalıyor.
Burada ihtiyacımız olan şey, kullanan tarafın "bana çeviri lazım" diyebilmesi, ama bunun hangi yoldan geleceğini söylememesi. Bu ayrımın C#'taki karşılığı interface. ITranslator diye bir interface tanımlarsak, kullanan sınıf sadece Get ve Use methodlarının var olduğunu bilir; arkasında dosya mı var, sözlük mü var, bilmez ve bilmesi de gerekmez.
İkinci bir implementasyon yazdığımız anda bu iddia somutlaşıyor. FileTranslator bugünkü davranışı, yani dosyadan okumayı taşısın. Yanına da mesajları kod içinde tutan ikinci bir sınıf koyalım. İkisi de ITranslator'ı uyguladığı için kullanan taraf açısından aralarında hiçbir fark yok. Değişen tek şey, container'a hangisini oluşturacağını söylediğimiz satır.
İlk yazıda DI'ı "artık new yazmıyoruz" üzerinden anlatmıştım. Test edilebilirlik argümanı ise ancak burada, elimizde gerçekten iki implementasyon varken bir anlam kazanıyor.