Dependency Injection(DI) Nedir? Nasıl Uygulanır?

Merhaba,

Bu yazımızda Martin Fowler’in ortaya attığı Dependency Injection(DI) – Bağımlılık Enjeksiyonu kavramını inceleyecek ve nasıl uygulandığını örneklendireceğiz. Öncelikle “Nedir bu Dependency Injection?” sorusuyla başlayalım.

Dependency Injection’ı özetle anlatmak gerekirse; bağımlılık oluşturacak parçaların ayrılıp, bunların dışardan verilmesiyle sistem içerisindeki bağımlılığı minimize etme işlemidir. EEee yani!

Yani, temel olarak oluşturacağınız bir sınıf içerisinde başka bir sınıfın nesnesini kullanacaksanız new anahtar sözcüğüyle oluşturmamanız gerektiğini söyleyen bir yaklaşımdır. Gereken nesnenin ya Constructor’dan ya da Setter metoduyla parametre olarak alınması gerektiğini vurgulamaktadır. Böylece iki sınıfı birbirinden izole etmiş olduğumuzu savunmaktadır. Ha doğru mudur?, dibine kadar doğrudur…

Daha da özetlememiz gerekirse yazılımı oluşturan yapılar kaçınılmaz olarak birbirleri ile ilişkilidir. Lakin bu ilişkinin bir bağa ve sınırlandırmaya sebep olmaması için mümkün mertebe ilişkiyi gevşek tutmak önemlidir. Biz buna Loosely Coupled yani Gevşek Bağlılık diyoruz.

Bundan dolayı yazılımı oluşturan yapıların birbirleri ile olan sıkı bağ azalacağı için uygulamaya yeni özellikler eklenip çıkartılabilmesi kolay hale gelecektir. Bu durumu şöyle örneklendirelim.

Elimizde “A” ve “B” sınıfı olsun. “A” sınıfında “B” nesnesi üretildiğini düşünün. Yazımızın devamında da örneklendireceğimiz gibi “A” sınıfı “B” sınıfına bağlı hale gelmiştir. Gün geldi “B” nesnesinde bir değişiklik olduğu zaman mecbur gidip “A” sınıfında da bu değişiklik üzerine çalışma yapmamız gerekecektir. Tamam, eğer burada konuştuğumuz gibi projeniz iki sınıftan ibaretse hiç Dependency Injection mevzusuna girmeden gidip “A” sınıfında değişikliği yapmanız mantıklıdır. Ama yüzlerce class’lardan oluşan bir proje için bunu denemenizi pek tavsiye etmem. Eee, peki ne yapacağız? diye sorarsanız eğer, “A” nın “B” ye olan bağımlılığını minumuma indirgeyeceğiz. Tabi bunun nasıl yapıldığını yazının devamında irdeleyeceğiz.

İşte bu tarz bir durumda Dependency Injection tekniğini uygulayarak uygulama içerisinde değiştirilmesi, müdahale edilmesi gereken yerler minumuma inecektir.

Dependency Injection, bağımlılıkları soyutlamak demektir.

Dependency Injection’ın teferruatlarına girmeden önce dikkat etmeniz gereken husus şudur ki; Dependency Injection çoğu zaman Dependency Inversion ile karıştırılır. Fakat Dependency Inversion problem çözmeye yarayan bir prensip iken Dependency Injection ise bu prensibi uygulayan bir Design Pattern’dir.

Şimdi Dependency Injection’ın tasarımını uygulama durumlarını konuşalım…

DI, aşağıdaki iki teknikle uygulanabilmektedir.

  • Constructor Injection(Constructor Based Dependecy Injection)
  • Setter Injection(Setter Based Dependency Injection)

Sizlere iki yöntemle örnek durumlarıda göstereceğim. Şimdi DI desenini uygulayabileceğimiz durumları pratik olarak inceleyelim.

    class Araba
    {
        public void GazVer()
        {
            //...
        }
        public void FrenYap()
        {
            //...
        }
        public void SagaSinyal()
        {
            //...
        }
        public void SolaSinyal()
        {
            //..
        }
    }

    class Vasita
    {
        Araba araba;
        public Vasita()
        {
            araba = new Araba();
        }

        public void Kullan()
        {
            araba.GazVer();
            araba.SagaSinyal();
            araba.FrenYap();
            araba.SolaSinyal();
        }
    }

Yukarıdaki kod bloğunu incelerseniz eğer yazının başında da anlattığım gibi “Vasita” sınıfı “Araba” sınıfına bağlı bir vaziyet arz etmektedir. Yani ben ne zaman “Araba” sınıfında bir değişiklik yapsam gelip “Vasita” sınıfında da o değişikliğe göre çalışma gerçekleştirmem gerekebilir. Haliyle onlarca sınıf söz konusu olduğu durumlarda bu pek mümkün olmayacaktır.

Şimdi düşünün ki, “Vasita” sınıfına “Araba” yerine “Otobus” classı vermek durumunda kalırsam eğer gelip burada ki komutları güncellemek zorunda kalacağım. Haliyle sabahtan beri bahsettiğim onlarca sınıf durumunda bağımlılık arz eden sınıflarda güncelleme yapmak hiç yazılımcı işi değildir.

İşte… “Araba sınıfı istediği kadar değişsin ama Vasita sınıfının bundan haberi olmasın. Haberi olmasın ki Vasita sınıfıyla uğraşmak zorunda kalmayayım” diyorsak eğer Dependency Injection(DI) tasarımını uygulayacağız.

Şimdi DI desenini uygulamak için bir interface tanımlayacağız. Tanımladığımız bu Interface’den kalıtım alan tüm sınıflar doğal olarak Interface’in kalıbını, şartını sağlayacaktır.

    interface ITasit
    {
        void GazVer();
        void FrenYap();
        void SagaSinyal();
        void SolaSinyal();
    }

Evet, gördüğünüz gibi Interface’imizi oluşturduk. Şimdi “Araba”, “Otobus”, “Motor” vs. gibi taşıtlarımızı bu Interface’den türeteceğiz.

    class Araba : ITasit
    {
        public void GazVer()
        {
            //...
        }
        public void FrenYap()
        {
            //...
        }
        public void SagaSinyal()
        {
            //...
        }
        public void SolaSinyal()
        {
            //..
        }
    }

    class Otobus : ITasit
    {
        public void GazVer()
        {
            //...
        }
        public void FrenYap()
        {
            //...
        }
        public void SagaSinyal()
        {
            //...
        }
        public void SolaSinyal()
        {
            //..
        }
    }

    class Motor : ITasit
    {
        public void GazVer()
        {
            //...
        }
        public void FrenYap()
        {
            //...
        }
        public void SagaSinyal()
        {
            //...
        }
        public void SolaSinyal()
        {
            //..
        }
    }

Şimdi vakit “Vasita” sınıfımıza DI desenini uygulamaya geldi.

    class Vasita
    {
        ITasit _tasit;
        public Vasita(ITasit tasit)
        {
            _tasit = tasit;
        }

        public void Kullan()
        {
            _tasit.GazVer();
            _tasit.SagaSinyal();
            _tasit.FrenYap();
            _tasit.SolaSinyal();
        }
    }

Yukarıdaki kod bloğunu incelerseniz eğer, “ITasit” Interface’inden kalıtım alan herhangi bir sınıf “Vasita” sınıfına bir taşıt olabilir. Haliyle siz proje sürecinde “Vasita” sınıfına ister “Araba”, isterseniz “Otobus” yahut “Motor” verebilirsiniz. Nede olsa hepsi aynı metod ve işlevleri barındırdığı için hangi nesneyi verirseniz verin Interface referansı üzerinden ilgili nesne çalıştırılacaktır.

Olaya “Vasita” sınıfı açısından bakarsak eğer, kendisine verilen nesnenin ne olduğuyla ilgilenmemekte ve bağlı olduğu bir sınıf bulunmamaktadır. Yani olay araba, otobüs veyahut motor da olsa bu elemanların çalışma ve işleyişiyle ilgilenmemektedir. Nihayetinde araba ve otobüste gaz vermek için pedala basılırken, motorda sağ kolu çevirmek lazımdır. İşte Dependency Injection sayesinde “Vasita” sınıfı bu işlevlerin nasıl yapıldığıyla ilgilenmemekte, sadece ve sadece gelen araç hangisi olursa olsun gaz vermekte, frene basmakta ve sağa sola sinyal çakmaktadır. Eğer DI uygulanmazsa, “Vasita” gaz verme işleminde araba ve otobüs için pedalla, motor içinse sağ kolla ilgilenecektir.

Anlayacağınız “Vasita” sınıfı Dependency Injection sayesinde üzümü yiyor bağını sormuyor, farklı bir sınıfa olan bağını minimize etmiş oluyor.

Bu tasarım neticesinde aşağıdaki şekilde kullanım gerçekleştirilebilir.

    class Program
    {
        static void Main(string[] args)
        {
            Vasita vasitaAraba = new Vasita(new Araba());
            vasitaAraba.Kullan();
            //veya
            Vasita vasitaOtobus = new Vasita(new Otobus());
            vasitaOtobus.Kullan();
            //veya
            Vasita vasitaMotor = new Vasita(new Motor());
            vasitaMotor.Kullan();
        }
    }

Şuana kadar sizlere Dependency Injection’ı Constructor Injection(Constructor Based Dependecy Injection) yöntemi ile göstermiş oldum. Setter Injection(Setter Based Dependency Injection) yöntemi ile DI’yı uygulamak istiyorsanız eğer sadece “Vasita” sınıfında(yani projenizde DI’yı uyguladığınız sınıfta) aşağıdaki değişikliği yapmanız yeterlidir.

    class Vasita
    {
        public ITasit _tasit { get; set; }

        public void Kullan()
        {
            _tasit.GazVer();
            _tasit.SagaSinyal();
            _tasit.FrenYap();
            _tasit.SolaSinyal();
        }
    }

Yukarıdaki gibi Setter Injection yöntemi uyarlandıysa kullanımı aşağıdaki gibi yapmalısınız.

    class Program
    {
        static void Main(string[] args)
        {
            Vasita vasitaAraba = new Vasita();
            vasitaAraba._tasit = new Araba();
            vasitaAraba.Kullan();
            //veya
            Vasita vasitaOtobus = new Vasita();
            vasitaOtobus._tasit = new Otobus();
            vasitaOtobus.Kullan();
            //veya
            Vasita vasitaMotor = new Vasita();
            vasitaMotor._tasit = new Motor();
            vasitaMotor.Kullan();
        }
    }

Gördüğünüz gibi Dependency Injection kodunuzu özgürleştirmekte, sınıflar arasındaki bağı minimize etmektedir. Sınıflar çalıştırılırken üzümü yiyen amma bağını sormayan olgunlukta olurlar. Ama bunun yanında Visual Studio’nun “Go To Definiton” özelliğinide boşa çıkaran bir yapı olduğunu söyleyebilirim. Çünkü DI uyguladığınız sınıfta ilgili Interface’e “Go To Definiton” dediğiniz zaman sizi Interface’e gönderecektir. Eee haliyle hangi sınıfa bu Interface’in uygulandığını bulabilmeniz bazen işgenceye dönebilir. Hele hele bir başkası tarafından tasarlanmış proje üzerinde çalışma gerçekleştiriyorsanız….

Hepinize iyi çalışmalar dilerim…

Sonraki yazılarımda görüşmek üzere…

Bunlar da hoşunuza gidebilir...

12 Cevaplar

  1. Ahmet dedi ki:

    Hocam Merhaba,
    Yazılarınız çok güzel tevazünüz daha da güzel böyle insanlar tanımak o kadar güzel bir duygu ki. Yazılarınız çok güzel bir çırpıda okudum hepsini sürekli takipçinizim.

    • Gençay dedi ki:

      Teşekkür ederim Ahmet kardeşim… Çok sevindirdin beni Allah razı olsun. Bol bol faydalanman dileğiyle…

      Sevgi, Saygı ve Hürmetlerimle…

  2. fehmi k dedi ki:

    Bu konuda okuduğum onlarca makale arasındaki en açıklayıcı olanlardan birisiydi. Diliniz çok iyi ve anlaşılır, kitap yazmayı düşünmelisiniz bence.

    • Gençay dedi ki:

      Teşekkür ederim Fehmi Bey,

      Ben bu yorumu yazmış olmak için yazmadığımdan hak ediyorum sanırım. Bir olguyu, görülmesi gerekenleriyle gösterebilmek ve öğrenilmesi gerekenleriyle öğretebilmek için çabalıyorum. Haliyle ortaya bazen yamuk bazen düz bazen de zengin bir kaynak çıkıyor. Siz ki nadir feraset sahibi insanlarımız güzele ayrı bir güzellikle karşılık verdiğinizden dolayı teşekkür ediyorum. Var olun.

      Sevgilerimle…

  3. İlhan dedi ki:

    Hocam yazınız için çok teşekkür ederim. Konuyu daha iyi kavramak için bir sorum olacak. DI dediğimiz zaman şunu anlayabilir miyiz. Bir sınıf içerisinde başka bir sınıfı new diyerek doğrudan kullanmak yerine, kullanmak istediğimiz sınıfın önce interface ini oluşturup sonra o interface i enjekte etmeliyiz. Bu yaklaşım doğrumudur veya eksik yönleri varmıdır?

    • Gençay dedi ki:

      Tabi ilgili sınıf interface’den türetilmek şartıyla genel manada doğru mantıktasınız. Detaylar ise zaten bu mantığa paralel olmak üzere makalede değerlendirilmektedir.

      İyi çalışmalar…

  4. Mehmet Gökhan Özen dedi ki:

    Anlatımda güzel örneklendirmeler ve sade bir dil kullanmışsınız bu yüzden zevkle okudum, teşekkürler 🙂

  1. 10 Nisan 2016

    […] Dependency Injection yaklaşımı ile Factory Method Design Pattern arasında bir bütünlük sağlayarak bir proje üzerinde beraber nasıl bir tasarım gerçekleştirebiliriz? sorusuna cevap vermeye çalışacağız. […]

  2. 10 Nisan 2016

    […] maksatlı bir hatırlatmada bulunmak maksadıyla, Strategy Design Pattern’i uygularken Dependency Injection yaklaşımını sergilediğimize dikkat ettiniz mi? merak ediyorum […]

  3. 22 Nisan 2016

    […] yazılarımdan Dependency Injection(DI) Nedir? Nasıl Uygulanır? başlıklı yazımda Dependency Injection desenine detaylıca değinmiştik. Bu yazımızda ise bu […]

  4. 28 Eylül 2018

    […] kendini soyutlamamakta, programatik açıdan da tüm bağımlılığa aykırı olan Dependency Injection alt yapısında bir yaklaşım sunmaktadır. Bu içeriğimizde Asp.NET Core 2 MVC projesinde […]

Bir cevap yazın

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

*

Copy Protected by Chetan's WP-Copyprotect.