İlkay İlknur

just a developer...

Dependency Injection Nedir ? - C# ile Örnek Uygulama

Merhaba Arkadaşlar,

Yazılım geliştirme süreçleri içerisinde en önemli kısımlardan biri de yazılımın tasarım sürecidir. Genelde ülkemizde her ne kadar tasarım yapılmadan doğrudan (bodoslama) :) kodlama kısmına geçilse de kodun ilk yazılması sırasında yapılmayan tasarımın acısı genelde ilerleyen süreçlerde ürünler geliştikçe, yeni istekler geldikçe ortaya çıkmakta.

Yazılım tasarım presiplerinden biri de yazılım içerisinde bulunan componentlerin “loosely coupled”(gevşek bağlı) olmasıdır. Bu şekilde yapılan yazılım tasarımlarında yapılar birbirine sıkı sıkıya bağlı olmadığından dolayı gelebilecek olan yeni taleplerde uygulamalar içerisinde yapılacak olan değişiklikler de minimuma inecektir.

Şimdi gelin basit bir uygulama üzerinde bir yazılım içerisindeki bağımlılıkları inceleyelim.

Örnek uygulamamız içerisinde akışlarımız sırasında loglama ve mesaj gönderme kısımlarının olduğunu düşünelim ve şu şekilde ilgili işlemlerden sorumlu olan sınıflarımız olduğunu varsayalım.

Database loglaması İçin Kullanılabilecek Olan DBLogger Sınıfı

class DBLogger
{
    public void WriteLog(string message)
    {
        Console.WriteLine(String.Format("DBLogger : {0}", message));
    }
}

Mail Gönderimi İçin Kullanılabilecek Olan MailSender Sınıfı

class MailSender
{
    public void SendMessage(string message)
    {
        Console.WriteLine(String.Format("MailSender : {0}", message));
    }
}

Ayrıca hayal ettiğimiz uygulamamızın ana akışını içerisinde bulunduran Processor isimli sınıfı da şimdilik şu şekilde geliştirebiliriz.

class Processor
{
    public void Process()
    {
        DBLogger logger = new DBLogger();
        logger.WriteLog("Log Text");
        //Ana Uygulama Akışı          
        MailSender messageSender = new MailSender();
        messageSender.SendMessage("Message Text");
    }
}

Son olarak uygulamamızı basit bir console uygulaması olarak çalıştıracağımız için yazacağımız basit çağrım ise şu şekilde olacak.

static void Main(string[] args)
{
    Processor processor = new Processor();
    processor.Process();
}

Evet, şimdilik kod kısmının sonuna geldik. Kısaca simule ettğimiz senaryodan bahsedersek, processor isimli sınıfımız bizim business(iş) katmanı dediğimiz görevi üstlenerek uygulamanın ana akışını yönetmekten sorumlu. Uygulama akışı içerisinde ise öncelikle DBLogger sınıfı kullanılara database’e loglama yapılmakta, daha sonra ise uygulama akışı bittikten sonra MailSender sınıfı kullanılarak Mail gönderimi yapılmakta. Uygulamayı çalıştırdıktan sonra şu şekilde bir ekran görüntüsü elde edeceğiz.

Buraya kadar olan kısımda klasik bir biçimde tüm isteklerin sabit olduğunu varsayarak uygulamamızı geliştirdik. Peki uygulamamızı başka bir kuruma verdiğimizi düşünelim ve bu kurumun loglama için dosya sistemini kullandığını ve Mail yoluyla mesajları almak istemediğini ve SMS sistemini tercih ettiğini düşünelim.

Öncelikli olarak yapacağımız şey tabi ki de dosyaya loglama yapacak ve mesajları SMS yoluyla gönderecek olan bileşenleri geliştirmek.
class FileLogger
{
    public void WriteLog(string message)
    {
        Console.WriteLine(String.Format("FileLogger : {0}", message));
    }
}
class SMSSender
{
    public void SendMessage(string message)
    {
        Console.WriteLine(String.Format("SMSSender : {0}", message));
    }
}

Evet, belirtilen isteklere uygun olarak geliştirmelerimizi tamamladık. Şimdi sıra geldi bu kısımları iş akışımız içerisine yerleştirmeye.

class Processor
{
    public void Process()
    {
        //DBLogger logger = new DBLogger();                   
        FileLogger logger = new FileLogger();
        logger.WriteLog("Log Text");
        //Ana Uygulama Akışı          
        //MailSender messageSender = new MailSender();
        SMSSender messageSender = new SMSSender();
        messageSender.SendMessage("Message Text");
    }
}

İş akışımız içerisinde database’e loglama yapan kısmı ve Mail gönderen kısmı commentleyerek yeni geliştirmiş olduğumuz bölümleri uygulamaya ekleyerek uygulamamızı çalıştırdık ve ihtiyaçları karşıladık. Peki başka bir kurum da dosya loglaması ve Mail ile mesajlaşma isterse yine gereken kısımları commentleyip uygulamamızı tekrardan mı build edeceğiz !!!!

Hımmm… Sanki birşeyler yanlış gidiyor. Kullanıcıların istekleri değişiklik gösteriyor ve biz anında bu isteklere adapte olamıyoruz. Peki neyi yanlış yapıyoruz !!!

Uygulama Bağımlılıkları ve Dependency Injection

Yukarıda yaptığımız uygulamaya baktığımızda uygulamamızın bir takım sınıflara sıkı sıkıya bağlı olduğunu görmekteyiz. Yani uygulamamızın akışını radikal değişiklikler yapmadan değiştiremiyoruz. Bu da uygulamamızın genişletilebilir olmasını çokta fazla mümkün hale getirmiyor.

Peki ne yapabiliriz ?

Baktığımız zaman aslında farklı akışlarımız olsa da bu akışlar temelde bir işi farklı bir biçimde gerçekleştirmekte. Biri mesaj gönderme işini SMS veya E-Mail yoluyla yaparken diğeri loglama işlemini database veya dosya olmak üzere farklı lokasyonlar üzerinde yapmakta. Bu noktada aklımıza gelen ilk iyileştirme bu işlemleri bir interface üzerinden yönetmek olmalı.

Öyleyse hemen uygulayalım.

interface IMessageSender
{
    void SendMessage(string Message);
}
interface ILogger
{
    void WriteLog(string message);
}
class DBLogger : ILogger
{
    public void WriteLog(string message)
    {
        Console.WriteLine(String.Format("DBLogger : {0}", message));
    }
}
class MailSender : IMessageSender
{
    public void SendMessage(string message)
    {
        Console.WriteLine(String.Format("MailSender : {0}", message));
    }
}
class FileLogger : ILogger
{
    public void WriteLog(string message)
    {
        Console.WriteLine(String.Format("FileLogger : {0}", message));
    }
}
class SMSSender : IMessageSender
{
    public void SendMessage(string message)
    {
        Console.WriteLine(String.Format("SMSSender : {0}", message));
    }
}

Gördüğünüz gibi uygulamamız içerisinde interface’leri kullanarak hiyerarşik bir yapı oluşturduk ve yeniden kullanımın mümkün olduğu bir yapı geliştirdik. Şimdi gelelim iş akışımızın bulunduğu Processor isimli sınıfımıza. Bu sınıf içerisinde iş akışımız yukarıdaki durumda daha önceden belirlenmiş bir şekilde gidiyordu. İşte şimdi tam bu noktada yarattığımız interface’ler sayesinde uygulamamızın akışını istediğimiz gibi dinamik olarak belirleyebiliriz.

class Processor
{
    ILogger logger = null;
    IMessageSender messageSender;
    public Processor(ILogger _logger, IMessageSender _messageSender)
    {
        logger = _logger;
        messageSender = _messageSender;
    }
    public void Process()
    {
        logger.WriteLog("Log Text");
        //Ana Uygulama Akışı                    
        messageSender.SendMessage("Message Text");
    }
}

Gördüğünüz gibi yukarıda ilgili loglama ve mesaj gönderme nesnelerin yaratılmasını farklı metotlara aktardık. Peki bu metotların içerisi nasıl olacak ? (Hangi tiplerin yaratılacağını basit bir şekilde config dosyasından alıyoruz)

static IMessageSender CreateMessageSender()
{
    IMessageSender retVal;
    string messageSender = ConfigurationManager.AppSettings["message"].ToString();
    if (messageSender == "SMS")
    {
        retVal = new SMSSender();
    }
    else
    {
        retVal = new MailSender();
    }
    return retVal;
}
static ILogger CreateLogger()
{
    ILogger retVal;
    string logger = ConfigurationManager.AppSettings["logger"].ToString();
    if (logger == "DB")
    {
        retVal = new DBLogger();
    }
    else
    {
        retVal = new FileLogger();
    }
    return retVal;
}

Şimdi sıra geldi akışın dinamik olarak yaratılmasına ve çalıştırılmasına. Yukarıda dikkat ettiyseniz Processor sınıfının constructor’ı içerisinde çalıştıracağı tipleri parametre olarak almakta. Yani biz eğer parametre olarak constructor’da belirtilen tipi implemente eden bir tipi geçirirsek uygulamamız otomatik olarak interface üzerindeki metodu çağırdığından dolayı uygulamamızın akışı doğrudan ilgili tipin içerisinde bulunan akışa doğru yönlenecektir.

static void Main(string[] args)
{
    Processor processor = new Processor(CreateLogger(), CreateMessageSender());
    processor.Process();
}

Evet arkadaşlar işte yukarıda uyguladığımız yapıya Dependency Injection diyoruz. Dependency Injection, Inversion of Control adını verdiğimiz uygulama akışını değiştirme yönteminin özelleşmiş bir halidir. Dependency Injection ile uygulamanın çalışacağı ve bağımlı olduğu akışları dışarıdan enjekte ederek uygulama akışını dinamik olarak değiştirebiliyoruz. Böylece uygulamamızın genişletilebilmesi ileri zamanlarda gelebilecek olan yeni geliştirmelerde uygulamamızın en az oranda etkilenmesini mümkün kılabilmekteyiz.

Bitirirken

Üniversite yıllarında(çok uzun değil sadece 1 sene önce :) ) aldığım yazılım tasarımı dersinde uygulama içerisindeki bağımlılıkları tanımlarken “bilmek” kelimesini kullanırdık. Yani ilk uygulamada baktığımızda Processor tipi DBLogger ve MailSender tiplerini biliyordu. Ancak son yaptığımız geliştirmeye bakarsanız Processor sadece temel interface’leri biliyor. Yani bu interface’leri implemente eden ister 100 ister 1000 adet tip olsun ve içlerinde yaptıkları akışlar farklı olsun bu durum artık kesinlikle processor sınıfını ilgilendirmiyor.

Evet arkadaşlar özellikle genişlemesi olası olan uygulamalar yazıyorsanız veya isteklerin çeşitleneceği noktalar üzerinde çalışıyorsanız Dependency Injection kullanmanızı mutlaka tavsiye ederim. Böylece en az acı ile uygulamalarınızda gerekli değişiklikleri gerçekleştirebilirsiniz. ;)

Hoşçakalın,

Not : Blog yazıları haricinde sosyal medya üzerinden anlık paylaşımlarımı Twitter ve Facebook'tan takip edebilirsiniz.



Yorum Gönder


Yorumlar

  • profile

    Nurlan Ezade

    • 6
    • 4
    • 2017

    Gercekten de guzel bir anlatim olmus. tesekkur ederim

  • profile

    ahmet yaman

    • 27
    • 3
    • 2017

    Güzel bir yazı olmuş.. Teşekkür ederim.

  • profile

    Alex DERREN

    • 23
    • 3
    • 2017

    Turkce makaleler arasinda bu konuda okudugum en net, anlasilir ve ifadeleri yalin ama bir o kadar da zengin dokuman diyebilirim. Suleyman BARLAS Bey'in yorumuna da katilmakla birlikte; Kodlarin gosterim sekli, yazi icerisindeki kalin (bold) vurgu ifadeleri ve hikayeye yer vermeyen sade, konuya tam odakli aciklamalar muazzam. Tebrikler.

  • profile

    süleyman barlas

    • 25
    • 11
    • 2016

    yazılımla ilgili makalelerin çoğunda türkçe problemi yaşanıyor kanımca. siz gayet anlaşılır anlatmışsınız. çok teşekkürler.

  • profile

    Mr Hoffman

    • 15
    • 11
    • 2016

    Eline sağlık dostum. Makalen çalıştığım bi yerde çok kez kullandığım ama tam olarak ne işe yaradığını bilmediğim bi yapıyı fazla zamanımı almadan anlamama yardımcı oldu. Başarılarının devamını diliyorum.

  • profile

    Alican Çevik

    • 6
    • 11
    • 2016

    Tebrikler çok güzel bir anlatım olmuş, teşekkürler.

  • profile

    yasar taymaz

    • 29
    • 9
    • 2016

    tebrik ederim herhalde daha guzel anlatilamazdi.

  • profile

    İlkay İlknur

    • 4
    • 9
    • 2016

    Selamlar Birisi, Keşke adını yazmaya cesaret edebilseydin de adınla hitap edebilseydim. Neyse... Yazıyı yanlış anlamana üzüldüm. Dependency injectionın benim yazdığım interfacelerle bir ilgisi var tabi ki. Çünkü yazının başlığından da anlayabileceğin üzere C# ile örnek uygulama yazarak anlatmaya çalışıyorum. Ancak temel anlamda uyguladığımız yöntemin dependecy injection olduğundan bahsediyorum. Bir daha böyle sorunlar yaşamaman için yeni öğrendiğin konuları birden fazla farklı kaynaktan okumanı tavsiye ederim.

  • profile

    Birisi İkisi

    • 3
    • 9
    • 2016

    Dependency injection bağımlılığı parametre olarak göndermektir. Bu kadar basit. Öyle bir yazmışsın ki sanki dependency injection'ın senin tanımladığın interfaceler ile bir ilgisi varmış gibi anlaşılıyor. Böyle anlayıp bir iş görüşmesine gittim ve adam bana bu dependency injection değil dedi.Birde yazdığın yazı Google da en üstlerde çıkıyor aratınca. Bence bu konuyu tekrar bir araştır öyle yaz. Burdaki insanları yanlış yönlendiriyorsun.

  • profile

    İlkay İlknur

    • 4
    • 5
    • 2016

    @Yaşar Cemal Kodları düzenledim. Eski blog sisteminden yeni blog sistemine geçerken kodların görünümü bozuldu. Belli bir sıraya kadar toparladım ama çok eski yazıları bıraktım. Bu güncel bir konu çok rağbet görüyor. Düzenledim kodları. Teşekkürler haber verdiğin için.

  • profile

    yaşar cemal

    • 4
    • 5
    • 2016

    selam çok güzel bir yazı yazmışsınız fakat kodlar çok dağınık herhangi bir editör içinde düzenleme şansınız yok mudur? kodlar okunaklı olmadığı için anlaşılamıyor

  • profile

    Salih KARAHAN

    • 27
    • 1
    • 2014

    Merhaba hocam, Bu haricinde üç beş yazı daha okudum ama sizinkinin daha açıklayıcı olduğunu düşündüm. Teşekkürler.

  • profile

    İlkay İlknur

    • 27
    • 11
    • 2013

    Yapabilirsin. Ancak makalede de bahsettiğim gibi processor sınıfın bu tiplere bağımlı hale geliyor ve her yeni logic eklediğinde bu sınıflarda da değişiklik yapman gerekiyor. Diğer türlü yaptığında sadece ilgili interfaceten türeyen sınıfı yazıyorsun ve gerekli durumda da o sınıfı processora parametre olarak geçiyorsun. Böylece aslında baktığında processor sınıfı kendisine gelen tipe bağımlı olmuyor ve kendisine hangi tip verilirse verilsin o tipin ilgili metedunu çağırıyor. Bağımlılıklarının fazla olduğu ve değişiklik olmasının kaçınılmaz olduğu durumlarda bu yöntemi uygulayabilirsin. Ancak yazdığın kodun bir kısmında yeni eklemelerin olmayacağını garanti edebiliyorsan DI kullanmadan da ilerleyebilirsin. Sonuçta getirdiği ekstra bir maliyet var. Burada doğru kararı alabilmek önemli.

  • profile

    Oğuz Çelikdemir

    • 26
    • 11
    • 2013

    İlkay merhaba, bir konuda merakımı gidermek istiyorum. Verdiğin örneği referans alırsak, DI kullanmadan, sadece "config" ile bu bağımlılıkları gideremezmiydik? Yani, Interface kullanmak yerine, zaten yazmış olduğun metodları ( DB, SMS, File ) config' den gelecek parametreye görer Process içinden yönlendiremezmiydik? Farkındayım, config kullanmak uygulama için pek tercih edilen birşey değil fakat bu kadar yoğun kod bloğu ile uğraşmak beni açıkçası tedirgin etti.

  • profile

    mustafa

    • 30
    • 10
    • 2013

    Teşekkürler çok faydalı bir makale.

  • profile

    Batuhan

    • 18
    • 7
    • 2013

    Hocam,eline ağzına sağlık çok teşekkur ederim.Şuan staj yapiyorum ve ilgili mühendis dependency injection ı öğren dedi tonla yere baktım ama senin sununmunla tam oturdu tekrardan saol:D

  • profile

    İzzet

    • 7
    • 3
    • 2013

    Elinize sağlık çok faydalı güzel bir yazı olmuş. Tşk

  • profile

    gokhan t

    • 10
    • 1
    • 2013

    "Hocam çok teşekkürler. Bu konularda Türkçe makalelere, sade anlatımlara erişebilmek çok güzel. Paylaşımlarınız için minnettarım." aynen, çok teşekkürler

  • profile

    Burak TARHANLI

    • 7
    • 1
    • 2013

    Hocam çok teşekkürler. Bu konularda Türkçe makalelere, sade anlatımlara erişebilmek çok güzel. Paylaşımlarınız için minnettarım.

  • profile

    İlkay İlknur

    • 9
    • 11
    • 2012

    @Serdar Dll'i referans ettiğin projenin config dosyasında olmalı ayarlar.

  • profile

    Serdar

    • 7
    • 11
    • 2012

    Özürdilerim şöyle bir sorun yaşadım bu iki metodu classlibrar in için demi yoksa referans ettiğim projenin içinde ki config den mi alıcak static IMessageSender CreateMessageSender() { string messageSender = ConfigurationManager.AppSettings["message"].ToString(); if (messageSender == “SMS”) return new SMSSender(); return new MailSender(); } static ILogger CreateLogger() { string logger = ConfigurationManager.AppSettings["logger"].ToString(); if (logger == “DB”) return new DBLogger(); return new FileLogger(); }

  • profile

    Hakan Işık

    • 19
    • 9
    • 2012

    Teşekkürler , açıklayıcı bir yazı.

  • profile

    Bİrol Coşkun

    • 21
    • 8
    • 2012

    Gayet faydalı ve çözüm sunan bir yazı olmuş. Elinize sağlık. Çok işime yaradı.

  • profile

    yazu

    • 13
    • 7
    • 2012

    Güzel bir yazı emeğinize sağlık

  • profile

    Turgay

    • 12
    • 7
    • 2012

    Teşekkürler çok faydalı bir anlatım.

  • profile

    Birkan

    • 9
    • 7
    • 2012

    Gerçekten güzel bir yazı olmuş, açıkcası C#'da hiç uygulama geliştirmemiş bir olarak bile kodları okurken ve anlarken hiç zorlanmadım. Teşekkürler

  • profile

    eren çetin

    • 19
    • 6
    • 2012

    Yabancı bir kaynaktan okuduğum ve yarım yamalak anladığım bu yöntemi, doyurucu ve tutarlı bir şekilde kaleme aldığınız makalenizle pekiştirmiş oldum. Çok teşekkür ederim.

  • profile

    Bayram Üçüncü

    • 29
    • 10
    • 2011

    Yazınızın en başındaki küçük ayrıntıya dikkat çekerek, yani ülkemizde tasarım kalıpları ve düzgün kod yazma alışkanlığını kazanmak ve kazandırabilmek adına güzel bir giriş. Teşekkürler. Sırf bu konu üzerine bende blogumda yer vermek istiyorum ve kodlama standartları üzerine bir yazı dizisi hazırlıyorum. Bu öyle bir durum ki gerçekten, iki yazılımcı bi araya geldiğinde aynı dilden konuşamıyor. Tasarım kalıpları ve standartlar kullanılmadığı için herkes bir kod karmaşası içerisinde. Plansız yapılan her iş ise fazladan mesai ve zaman kaybı demek. Bence ülkemizin bu kadar vakti yok.

  • profile

    Ümit Doğan

    • 15
    • 10
    • 2011

    Çok sade ve açıklayıcı bir anlatım olmuş teşekkürler.

  • profile

    Mesut

    • 4
    • 10
    • 2011

    Güzel yazi evt, parametrik bi yapi kullanarak daha guzel islem yapilabilir. sonuc olarak configden de parametre niteliginde bir string e gore islem yaptiriyor

  • profile

    umit

    • 4
    • 10
    • 2011

    Elinize sağlık...Çok faydalı bir yazı olmuş