Dependency Injection Üzerine

   Eğer sizinle de bir nesne yönelimli programla oturumu yaptıysak mutlaka benden duymuşsunuzdur; Üniversite’de okurken nesne yönelimli programlama dersi hocamızın kulağımda yer etmiş bir sözü vardı; nesne yönelimli mimarilerin temel bir prensibi vardır : “SANANE”. Bu prensip olabildiğine basit; fakat bir o kadar da başarılı uygulamaların önünü açan bir yaklaşımdır. Aynı, hayatın kendisi gibi… Araba kullanıyorken gaza bastığınızda aracın nasıl hareket ettiğini tüm detayları ile bilmenize gerek yoktur ya da size tüm araçlarca sunulan direksiyon, gaz, fren, debriyaj, vites gibi temel arayüzler/kontratlar/sözleşmeler sizin aracı tamamen marka/model bağımsız olarak kullanmanız için yetmekte. Konuyu biraz detaylandırdığımızda temel yazılım prensiplerinden gevşek bağlı (loosely coupled) olmanın aslında “SANANE prensibi” ile nasıl özetlendiğini görebilirsiniz. Uygulamalarımızda oluşturduğumuz kontratlar bizleri arka plandaki karışık iş mantıklarından tamamen soyutlayacaktır. Nasıl’ına, Neden’ine odaklanmaksızın kendi uygulamamızı geliştirmeye odaklanmamıza imkân sunacaktırlar. İşte bu noktaya ortaya çıkan gevşek bağın oluşturulması/yönetilmesi ihtiyacı için pek çok farklı yöntem geliştirilmiştir. Temel amaç her zaman için bağımlılığın gevşek olması, soyutlanması olmuştur.

   Amaç bağımlılığın gevşek olması olunca, kaçınılmaz olarak, kontratlar/sözleşmeler üzerinden aldığımız hizmetleri yerine getirecek sınıfların oluşturulması işi sınıfımızın dışına taşınmaya başlamıştır. Bir başka değişle bu bağımlılıklarımız kontratlar vasıtasıyla uygulamamız/sınıfımız içerisine dışarıdan enjekte edilmiştir. Şimdi, bu noktada biraz nefes alıp aşağıdaki örneği inceleyelim;

[Serializable]
public class Ogrenci {
    public int No;
    public string Adi;
    public string Soyadi;
    public string Bolum;
}
 
internal class OgrenciIslemleri {
    private readonly XmlDosyaVeriDesposu veriDeposu;
 
    public OgrenciIslemleri() {
        veriDeposu = new XmlDosyaVeriDesposu();
    }
 
    public void OgrenciKaydet(Ogrenci ogrenci) {
        try {
            Console.WriteLine("Öğrenci kaydı yapılıyor...");
 
            var referansId = veriDeposu.Kaydet(ogrenci);
 
            Console.WriteLine("Öğrenci kaydı yapıldı. Referans Id : " + referansId);
        }
        catch (Exception istisna){
            Console.Error.WriteLine("Öğrenci kaydı sırasında beklenmeyen bir hata oluştu : " + istisna);
        }
    }
}

   Eminim ki; bu küçük kod parçacığınız incelediğinizde OgrenciIslemleri sınıfının XmlDosyaVeriDesposu sınıfına olan bağımlılığı dikkatinizi çekmiştir. Projeniz büyüdükçe OgrenciIslemleri sınıfının da orantılı olarak büyüyeceği, dolayısıyla XmlDosyaVeriDesposu sınıfının kullanımının da artacağı beklenecektir. Bu durum da ilerleyen zamanlarda projenizin veri kaynağının xml’den farklı bir veri kaynağına çevirmenin ne kadar zorlayıcı bir iş kalemi olacağını tahmin edersiniz. Öte yandan biz bunun yerine aynı işlemleri aşağıdaki örnekte olduğu şekliyle gerçekleştirelim;

public interface IVeriDeposu {
    long Kaydet(object veri);
}
 
internal class OgrenciIslemleri {
    private readonly IVeriDeposu veriDeposu;
 
    public OgrenciIslemleri(IVeriDeposu veriDeposu) {
        this.veriDeposu = veriDeposu;
    }
 
    public void OgrenciKaydet(Ogrenci ogrenci) {
        try {
            Console.WriteLine("Öğrenci kaydı yapılıyor...");
 
            var referansId = veriDeposu.Kaydet(ogrenci);
 
            Console.WriteLine("Öğrenci kaydı yapıldı. Referans Id : " + referansId);
        }
        catch (Exception istisna){
            Console.Error.WriteLine("Öğrenci kaydı sırasında beklenmeyen bir hata oluştu : " + istisna);
        }
    }
}

   Örneğimizde; bir Ogrenci POCO sınıfımız, bu sınıf üzerinde işlem yapan OgrenciIslemleri sınıfımız ve son olarak veriyi kaydedebildiğimiz bir IVeriDeposu kontratımız bulunmakta. Dikkat edecek olursanız ilk örnekten farklı olarak burada OgrenciIslemleri sınıfı ne veri kaynağını, ne de verinin bu veri kaynağına nasıl kayıt edildiğiyle ilgilenmemekte. Temel görevlerini yaparak ilgili noktalarda log atarak işlemini sonlandırmakta. Bu kod parçacığı en basit haliyle dışarıdan bağımlılığın enjekte edilmesi örnektir. Kod içerisinde kullanımını merak edenleriniz aşağıdaki örneği inleyebilirler;

class Program {
    static void Main(string[] args) {
        var ogrenci = new Ogrenci {
            No = 12345,
            Adi = "Fatih",
            Soyadi = "Boy",
            Bolum = "Bilgisayar Mühendisliği"
        };
 
        IVeriDeposu veriDeposu = new XmlDosyaVeriDesposu();
        var ogrenciIslemleri = new OgrenciIslemleri(veriDeposu);
 
        ogrenciIslemleri.OgrenciKaydet(ogrenci);
    }
}

   Bu kod parçacığı ile birlikte Öğrenci bilgileri bir xml dosyasına yazılabilmekte. Xml kaynağının nasıl kullanıldığını, performasının nasıl olduğunu merak edenlere yazımın başında paylaştığım hocamın sözünü hatırlatmak isterim 😉

   Ok, uygulamanızı geliştirdiğiniz ve veriniz gittikçe büyüdü.. Performans problemleri yaşamaya başladınız. Bu durumda doğaldır ki yapmak isteyeceğiniz ilk işlem veri kaynağı olarak xml dosyası yerine daha kabul görmüş çözümlere gitmek.. Veritabanı gibi.. Bu durumda yukarıdaki kod içerisinde tek değiştirmeniz gereken satır veri deponuzu ilklendirdiğiniz yer olacaktır;

class Program {
    static void Main(string[] args) {
        var ogrenci = new Ogrenci {
            No = 12345,
            Adi = "Fatih",
            Soyadi = "Boy",
            Bolum = "Bilgisayar Mühendisliği"
        };
 
        IVeriDeposu veriDeposu = new VeriTabaniDesposu();
        var ogrenciIslemleri = new OgrenciIslemleri(veriDeposu);
 
        ogrenciIslemleri.OgrenciKaydet(ogrenci);
    }
}

   Dikkat edecek olursanız asıl iş mantığınızın yer aldığı kodlar bu değişimden hiçbir şekilde etkilenmemekte.

   Örneğimizden devam edecek olursak; ilerleyen zamanlarda sql sorgularında derleyici desteğini de yanınıza almak istediğiniz ve Entity Framework kullanmaya karar verdiniz. Bu durumda sadece IVeriDeposu arayüzünü uygulayan yeni bir sınıf yaparak aşağıdaki şekilde kullanabilirsiniz;

class Program {
    static void Main(string[] args) {
        var ogrenci = new Ogrenci {
            No = 12345,
            Adi = "Fatih",
            Soyadi = "Boy",
            Bolum = "Bilgisayar Mühendisliği"
        };
 
        IVeriDeposu veriDeposu = new EntityFrameworkVeriDesposu();
        var ogrenciIslemleri = new OgrenciIslemleri(veriDeposu);
 
        ogrenciIslemleri.OgrenciKaydet(ogrenci);
    }
}

   Bu örnekler çoğaltılabilir; ama sanırım asıl vermek istediğim mesajı iletebildim. İş mantığı kodları ile veri kaynağı kodları arasındaki bağı IVeriDeposu arayüz tanımı sayesinde gevşek tutarak uygulamamı nasıl esnek hale getirebildiğimi gördünüz.

   Bu kadar örnek ardından, takip ettiğimiz bu yönteme literatürde Bağımlılık Enjeksiyonu (ya da daha bilinen adı ile Dependency Injection, DI) adı verilmekte. Hatta bir adım ileri gidecek olursak yukarıdaki örneğimiz yapıcı/ilklendirici üzerinden enjeksiyon (Constructer Injection) için güzel bir örnektir.

   Constructer Injection dışında Setter Injection’da oldukça sık kullanılan Dependency Injection yöntemlerinden birisidir. Constructer Injection’dan farklı olarak, Setter Injection’da bağımlılık yapıcı/ilklendirici üzerinden değil bir fonksiyon ya da özellik (property) üzerinden sınıfa iletilmektedir. Örneğin;

internal class OgrenciIslemleri {
    public IVeriDeposu VeriDeposu { set; get; }
 
    public void OgrenciKaydet(Ogrenci ogrenci) {
        try {
            Console.WriteLine("Öğrenci kaydı yapılıyor...");
 
            var referansId = VeriDeposu.Kaydet(ogrenci);
 
            Console.WriteLine("Öğrenci kaydı yapıldı. Referans Id : " + referansId);
        }
        catch (Exception istisna){
            Console.Error.WriteLine("Öğrenci kaydı sırasında beklenmeyen bir hata oluştu : " + istisna);
        }
    }
}

   İki yönetiminde kendilerine göre artı ve eski yanları bulunması nedeniyle kullanım amacına göre doğru yönetim tercih edilmesi önemlidir. Constructer Injection yaklaşımında bağımlılık bir kere ve sınıf oluşturulurken enjekte edilmektedir. Dolayısıyla sınıf için bağımlılığın sonradan değiştirilmesi söz konusu olmayacaktır. Diğer yandan Setter Injection yaklaşımında bağımlılık istendiği zaman değiştirilebilir ki bu durum da önemli bir hareket özgürlüğü sunmaktadır. Öte yandan Setter Injection yönteminde bağımlılığın kullanım öncesinde ilklendirilmiş olduğunun bir garantisi bulunmamaktadır.

   Dependency Injection kavramı ile tanışmamız ardından örneğimize geri döndüğümüzde iş mantığı kodumuz içerisinde loglama işlemlerini gerçekleştirdiğimizi hatırlayalım. Madem bağımlılıkları tamamen sınıfımızın dışına taşıyoruz, bu durumda loglama işlemini de dışarıdan enjekte etmek ileride işimize yarayacaktır;

internal class OgrenciIslemleri {
    private readonly IVeriDeposu veriDeposu;
	private readonly IGunlukDeposu gunlukDeposu;
	
	public OgrenciIslemleri(IVeriDeposu veriDeposu, IGunlukDeposu gunlukDeposu) {
		this.veriDeposu = veriDeposu;
		this.gunlukDeposu = gunlukDeposu;
	}

	public void OgrenciKaydet(Ogrenci ogrenci) {
		try {
			gunlukDeposu.Debug("Öğrenci kaydı yapılıyor...");
			
			var referansId = veriDeposu.Kaydet(ogrenci);
			
			gunlukDeposu.Debug("Öğrenci kaydı yapıldı. Referans Id : {0}", referansId);
		}
        catch (Exception istisna){
			gunlukDeposu.Error("Öğrenci kaydı sırasında beklenmeyen bir hata oluştu : {0}", istisna);
		}
	}
}

Fatih Boy

Ankara'da yaşayan Fatih, bir kamu kurumunda danışman olarak çalışmaktadır. ALM süreçleri, kurumsal veri yolu sistemleri, kurumsal altyapı ve yazılım geliştirme konularında destek vermektedir. Boş zamanlarında açık kaynak kodlu projeler geliştirmeyi ve bilgisini yazdığı makalelerle paylaşmayı seven Fatih, aynı zamanda Visual C# ve Visual Studio teknolojileri konusundan Microsoft tarafından altı yıl üst üste MVP (En Değerli Profesyonel) ödülüne layık görülmüştür. İş hayatı boyunca masaüstü uygulamaları, web teknolojileri, akıllı istemciler gibi konularda Asp.Net, Php, C#, Java programlama dilleri ve MySql, MsSql ve Oracle gibi veritabanı yönetim yazılımları ile çalışmıştır. İngilizce ve Türkçe olarak yayınlanan makalelerini gerek İngilizce bloğunda, gerekse de Türkçe bloğunda bulabileceğiniz gibi web sitesinden de açık kaynak kodlu geliştirdiği yazılımlarına ulaşabilirsiniz. vCard - Twitter - Facebook - Google+

1 Yorum

  1. Erhan   •  

    Sade ve başarılı bir anlatım, teşekkürler Fatih hocam.

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir