Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

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...

39 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 🙂

  5. Şaban Selvi dedi ki:

    Sayın Hocam ;
    Öncelikle emeğinize, yüreğinize sağlık. Allah işinizi rast getirsin…
    Şöyle bir sorum olacak;
    Bu sistemde örneğin sadece Otobüs için tanımlanmış, fazladan ” YolcuAl ( int yolcuSayisi ) ” gibi bir metot daha olsaydı o zaman nasıl bir yol izleyecektik =

    • Gençay dedi ki:

      Merhaba,

      Bahsettiğiniz duruma istinaden tasarım prensiplerinden Arayüz Ayrım Prensibi(Interface Segregation Principle – ISP) göz önünde bulundurularak ekstradan “YolcuAl” operasyonunu implemente edecek bir interface tanımlanabilir ve bu interface makalede bulunan “ITasit” interface’ini uygulayabilir. Bu durumda bağımsızlık amaçlanan sınıfta bu makalede olduğu gibi(Vasita sınıfı kastediliyor) Setter Injection yerine Constructor Injection yöntemiyle overloading uygulanarak hangi tipte nesnenin karşılanması gerektiği yazılımcının tercihine bırakılabilir. Bu senaryoyu netleştirebilmek için aşağıdaki somut örneği incelemenizi öneririm…

      Interfaceler;

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

      Concreate sınıflar;

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

      Yukarıdaki yapılanmayı incelerseniz eğer “YolcuAl” fonksiyonu sade ve sadece “Otobus” sınıfında mevcuttur. “Vasita” sınıfında ki düzenleme ise aşağıdaki şekilde olacaktır;

          class Vasita
          {
              public Vasita(ITasit tasit)
              {
                  tasit.GazVer();
                  tasit.SagaSinyal();
                  tasit.FrenYap();
                  tasit.SolaSinyal();
              }
      
              public Vasita(IYolcuAl yolcuAl)
              {
                  yolcuAl.GazVer();
                  yolcuAl.SagaSinyal();
                  yolcuAl.FrenYap();
                  yolcuAl.SolaSinyal();
                  yolcuAl.YolcuAl();
              }
          }
      

      Bu şekilde “Vasita” sınıfının constructer metoduna verilen nesnenin tipine göre ilgili overload tetiklenecek ve “Interface Segregation Principle” eşliğinde “Dependency Injection Design Pattern” uygulanılmış olunacaktır.

      Sevgiler.

  6. Burak dedi ki:

    Çok güzel vallahi şahane olmuş, gayet açık.

  7. ali dedi ki:

    Cok aciklayici ve sade. Cok tesekkurler… Allah razi olsun.

  8. Ahmet dedi ki:

    Mukemmel sade anlatim, cok tesekkurler, emeginize saglik

  9. Oğuzcan Genç dedi ki:

    Hocam merhaba. Burada kalıtım kullanarak da aynı işlemi yapabilirmiydik ? Farkları nedir DI ile ?

    • Gençay dedi ki:

      Merhaba,

      Evet… Kalıtımda kullanabilirsiniz. Ama kalıtım kullanırsanız, maliyet ve performans açısından negatif fark yaratmış olursunuz.

      Neden mi? Kalıtımın işlevsel özelliğinden dolayı…
      O özellikte şudur ki,

      bir sınıftan instance talep edildiğinde önce atalarından hiyerarşik olarak üretim gerçekleştirilir…

      Dolayısıyla aşağıdaki gibi

          class A
          {
      
          }
          class B : A
          {
      
          }
      

      aralarında kalıtımsal ilişki olan iki sınıfı ele alırsak eğer B’den her nesne üretildiğinde A’dan da bir nesne üretilmiş olacaktır. Halbuki interface’ler de implementasyon neticesinde herhangi bir nesne söz konusu olmayacağından dolayı burada ekstra bir maliyet oluşmayacaktır.

      Umarım aydınlatabilmişimdir 🙂

      İyi çalışmalar…

      • Oğuzcan Genç dedi ki:

        Tamamdır hocam çok iyi anlamış oldum teşekkür ederim. Hiyerarşik olduğu için tüm ara classlar newleneceği için hantal kalacak dediğiniz gibi

  10. özkan dedi ki:

    Bu konuyu öyle güzel ve açıklayıcı anlatmışsınız ki çok keyif alarak okudum ve anladım. Yazınız için çok teşekkür ediyorum. Elinize ve emeğinize sağlık hocam.

  11. furkan dedi ki:

    Merhaba hocam sorum şu ki sadece interface kullansaydık

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

    constructer üretmeyip yani dependency-injection kullanmasaydıkta aynı sonucu vermez miydi ? dependency-injection + olarak ne faydası oldu onu anlayamadım yardım ederseniz sevinirim . Bu bilgiler içinde çok teşekkürler .

    • Gençay dedi ki:

      Merhaba,

      Tabi ki de aynı sonucu verecektir. Ben makalede ilgili arayüzü constructor’dan parametre olarak istemişim. Siz verdiğiniz örnekteki gibi herhangi bir metottan yine aynı prensibe dayalı bu stratejiyi uygulayabilirsiniz. Bence olayı gayet güzel anlamışsınız ki bu şekilde ekstradan doğru bir örnek sunabiliyorsunuz 🙂 Maşallah diyelim…

      Zaten sizin yaptığınız da dependency injection’dır. Nihayetinde Kullan metodu herhangi bir taşıta bağlı olmamakta, ITasit‘tan türeyen tüm sınıfları karşılayabilmektedir. Hmm demek ki burada bağımlılık durumu tersine dönmekte ve geliştiricinin hangi taşıtı kullanacağını seçebilme hüvveti vermektedir. Böylece geliştirici herhangi bir taşıta bağımlı olmayacak, taşıtlar geliştiricinin tercihine bağlı olacaktır. Yani uzun lafın kısası bağımlılık olması gerektiği gibi tersine dönmüş olacaktır 🙂

      Dependency Injection’ın asıl artısını Clean Code yazarken göreceksiniz. Geleneksel katmanlı mimari olsun yahut Onion ya da Hexagonal mimariler olsun katmanlar arası bağımsızlığı sağlayabilmek ve geliştiricinin herhangi bir servise olan bağımlılığını ortadan kaldırarak, sadece iş mantığındaki tercihe odaklanabilmesini sağladığını ancak bu mimarisel yapılanmalarda anlayabilirsiniz. Hatta bu açıklama bile o kadar yetersiz ki!

      Esasında konuyu daha iyi anlayabilmek için NG Akademi bünyesinde konuya dair tarafımca kayda alınmış aşağıdaki videoyu izlemenizi öneririm :

      https://www.youtube.com/watch?v=Bhj2XdcoT2Q

      İyi çalışmalar…
      Sevgiler…

  12. Gamze dedi ki:

    Çok açıklayıcı olmuş. Teşekkürler…

  13. Kenan dedi ki:

    Merhaba Değerli hocam güzel insan…
    Eğitim serilerinizden .Net Core 5.0 bitirdim. DI yapısını her web uygulamasında kullanmaya çalışmalı mıyız? İlk olarak ;Kategori , ürün, fotoğraf, döküman,
    video ve stok yapısı gibi verileri tutan MVC yapısında entity kullanarak bir Admin panel tasarlıyorum (umarım biter:)
    CRUD işlemleri için bu yapıyı kurmaya çalışmam anlamlı olur mu?

    • Gençay dedi ki:

      Merhaba,

      DI’ı mümkün mertebe uygun noktalarda kullanmaya özen göstermekte fayda vardır. Bu pattern özünde SOLID prensiplerinden Dependency Inversion‘ın teknik boyutta uygulamasıdır. Haliyle prensip olduğu için uygulamakta fayda vardır.

  14. Bülent dedi ki:

    Bir konu bu kadar net ve açık anlatılamaz …Ellerinize sağlık.

  15. onur dedi ki:

    Yorumlar ayrı güzel, yazı ayrı güzel. Çok teşekkürler hocam.

  16. Emre dedi ki:

    Bir de method injection var.

  17. Semih dedi ki:

    Bir konu hakkında makale okumak istiyorsanız ilk olarak Gençay hocanın sitesinde var mı diye bi kontrol edin, daha sonra diğer sitelere yönelin. Benden size bir tavsiye 😀

  18. İbrahim dedi ki:

    Örnek güzel değil

  19. Fatih Uyanık dedi ki:

    Merhabalar hocam.
    Öncelikle hayırlı ramazanlar diliyorum.
    Birçok makale olkudum. ama yukarıdakiler gibi bende makaleyi çok açıklayıcı buldum. Emeğinize sağlık hocam.
    Hocam küçük bir rica mümkünse: Microsoft.Extensions.DependencyInjection ile birlikte mvvm için CommunityToolkit.mvvm pketlerinin kullanımına dair kısa bir makale kaleme almanız mümkün mü? Bu konuda işe yarar bir düzgün türkçe kaynak bulamadım. Mümkünse sevinirim hocam.
    kolay gelsin. İyi çalışmalar.

  20. atalay dedi ki:

    tam bir sun tzu yalınlığında, türk internet tarihinin en iyi dependency injection anlatımı.

  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 […]

  5. 09 Ekim 2021

    […] Tüm ziyaretçiler bu arayüzden türeyerek gerçek nesnelere birden fazla davranışı dependency injection sayesinde kazandırabilmemizi […]

onur için bir yanıt yazın Yanıtı iptal et

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir