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

Dependency Injection(DI) – Ninject

Merhaba,

Önceki 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 desene uygun tasarlanmış Inversion of Control(IoC) Framework’ü olan Ninject üzerine konuşacağız.

Biliyoruz ki, uygulama içerisinde nesne yaratma sürecinin sizden alınıp, bunun bir çatıya yani Framework’e devredilmesi işlemine Inversion of Control denmektedir. Dependency Injection deseninin ise işlevsel olarak, nesneler arasında bağımlılıkları soyutlama görevini yürüttüğünü biliyoruz. İşte Ninject – IoC Library’si, DI desenini basitçe uygulayabilmemizi ve kullanmamızı sağlayan hazır bir Container(kapsayıcı) olarak inşa edilmiştir.

Aslında bu işlem için geliştirilmiş pek çok Container mevcuttur. Ninject dışında; Castle Windsor, Spring.NET ve Unity kütüphanelerinede göz atabilirsiniz.(Bu içeriği oluşturduğum tarihlerde benimde bu kütüphaneler hakkında bir bilgim yoktur. Çalışma fırsatı yakalayıp sırra vakıf olmuşsam eğer yazıya dökmüş olurum inşallah.)

Şimdi Ninject librarysine gelmeden önce bu yapıyı kullanacağımız DI senaryosunu önceki yazımızdan aşağıya taşıyalım.

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

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

Önceki yazıyı incelerseniz eğer oluşturduğumuz DI tasarımında yukarıdaki yapıyı elde etmiştik. Tabi siz DI tasarım desenine hakimseniz yukarıdaki yapı sizin için senaryodan bağımsız bir anlam ifade edeceğinden önceki yazıyı okumanıza hiç gerek yoktur.

    class Program
    {
        static void Main(string[] args)
        {
            Vasita vasita1 = new Vasita(new Otobus());
            vasita1.Kullan();

            Vasita vasita2 = new Vasita(new Araba());
            vasita2.Kullan();

            Console.Read();
        }
    }

Yapılan tasarı bu şekilde kullanılabilmektedir.

Şimdi bu DI tasarımında daha hızlı ve aktif bir işlev sergileyecek olan Ninject kütüphanesini kullanalım.

Bu işlem için öncelikle Ninject kütüphanesini projeye entegre etmeliyiz.
Dependency Injection(DI) - Ninject

“Tools” -> “NuGet Package Manager” -> “Manage NuGet Packages for Solution”… kombinasyonlarını takip ederek NuGet pencersini açınız.(bilmeyenler için)

Dependency Injection(DI) - Ninject

Ekranda gördüğünüz gibi Ninject kütüphanesini Install ediniz.

Bu işlemler neticesinde Ninject kütüphanesini projenize başarıyla entegre etmiş oluyorsunuz.

Şimdi aşağıdaki kod bloğunu inceleyiniz.

    class Program
    {
        static void Main(string[] args)
        {
            #region Ninject İle Dependency Injection Kullanımı

            //Ninject çekirdeği oluşturuyoruz.
            IKernel kernel = new StandardKernel();

            kernel.Bind<ITasit>().To<Otobus>();

            var Vasita = kernel.Get<Vasita>();

            Vasita.Kullan();
            #endregion

            Console.Read();
        }
    }

Yukarıdaki kod bloğunu izah etmemiz gerekirse eğer;

  • 10. satırda, Ninject çekirdeği ITasit arayüzü gördüğünde Otobus sınıfından bir nesne bağlamaktadır.
  • 12. satırda, Ninject çekirdeğinden bir Vasita nesnesi talep edilmektedir.
  • 14. satırda, Ninject çekirdeğinden talep edilen Vasita nesnesinin Kullan metodu sorunsuz çalıştırılmaktadır.

İyi güzel… Peki arka planda nasıl bir mantık söz konusu?
Ninject çekirdeği, kendisine verilen arayüz ve sınıf arasında bir bağlantı kurmakta ve aynı çekirdekten talep edilen nesne üzerinde bu yapılar mevcutsa otomatik bu bağlantıyı sağlamaktadır. Yaptığımız işlemlere bakarsak eğer Ninject çekirdeğine, Vasita sınıfına özel bir tanımlama yapmadığımız halde kodumuz sorunsuz çalışmaktadır. Ninject çekirdeği, tanımlaması yapılmamış türler için akıllı bir strateji takip etmektedir. Get metodu ile talep edilen sınıfın tanımlaması bulunmuyorsa varsayılan tanım üzerinden işlemleri sorunsuz halledecektir. Vasita sınıfında da haliyle ITasit tipinde bulunan referansa, otomatik olarak Otobus nesnesini bağlamakta, haliyle talep edilen Vasita sınıfıda bu donanımda yaratılmaktadır.

Ayriyetten örnekte kullandığımız Vasita sınıfı parametreli constructer’a sahiptir. Lakin biz Get metodu ile Vasita sınıfından sorunsuz nesne talep edebildik. İşte bunu başaran mekanizma şu şekilde çalışmaktadır. Get metodu ile talep edilen sınıftan nesne oluşturmadan önce ilgili sınıfın constructer’ı kontrol edilmektedir. Eğer varsayılan constructer mevcutsa direkt ilgili nesneyi yaratacaktır. Yok eğer varsayılan constructer ezilmişse ve parametre alıyorsa en fazla tür bilgisi eşleştirmesine sahip olan constructer’ı kullanacaktır. Yani anlayacağınız, Ninject Vasita sınıfının constructer’ına bakıyor, ne alınmış ITasit tipinde bir parametre. Ninject çekirdeğinde tanımlaması yapılan arayüz tipide ITasit olduğu için bu eşleşme neticesinde bu constructer’ı otomatik olarak kullanıyor.

Şimdi, ITasit interface’i ile bağlantı kurduğumuz Otobus sınıfının constructer’ına aşağıdaki gibi yeni bir arayüz tanımlaması yapalım.

    interface IDurak
    {
        int Durak();
    }
    class ADurak : IDurak
    {
        public ADurak()
        {
            Durak();
        }
        public int Durak()
        {
            Console.WriteLine("A duraktayız.");
            return 10;
        }
    }
    class BDurak : IDurak
    {
        public BDurak()
        {
            Durak();
        }
        public int Durak()
        {
            Console.WriteLine("B duraktayız.");
            return 15;
        }
    }
    class CDurak : IDurak
    {
        public CDurak()
        {
            Durak();
        }
        public int Durak()
        {
            Console.WriteLine("C duraktayız.");
            return 20;
        }
    }
    class Otobus : ITasit
    {
        IDurak durak;
        public Otobus(IDurak durak)
        {
            this.durak = durak;
        }

        .
        . // ITasit'ın uygulattığı elemanlar
        .
    }

Yaptığımız bu işlem neticesinde projemizi derleyip çalıştırırsak eğer aşağıdaki hatayı alacağız.
Dependency Injection(DI) - Ninject

Şimdi bu hatayı anlatmadan önce Ninject’den nesne talep sürecinde iç içe bağ oluşturma yeteneğine değinmem gerekecek. Ninject çekirdeğine tanımlanan tipler içerisinde herhangi biri constructer’dan yine tanımlanmış bir tipi parametre olarak alıyorsa Ninject yapısal olarak ilgili tipte eşleşmeye en uygun constructer’ı tetikleyecektir. Yukarıdaki Ninject’in bu özelliğini kullanamamızın sebebi IDurak interface’ini belli bir tipe bağlamamızdan kaynaklanmaktadır.

    class Program
    {
        static void Main(string[] args)
        {
            #region Ninject İle Dependency Injection Kullanımı
            //Ninject çekirdeği oluşturuyoruz.
            IKernel kernel = new StandardKernel();

            kernel.Bind<ITasit>().To<Otobus>();
            kernel.Bind<IDurak>().To<ADurak>();

            var Vasita = kernel.Get<Vasita>();

            Vasita.Kullan();
            #endregion

            Console.Read();
        }
    }

Ninject çekirdeğine IDurak interface’i ile ADurak sınıfı arasında yeni bir bağlantı ekledim. Artık bu işlemden sonra projeyi derleyip çalıştırdığımızda sorunsuz işlediğini göreceksiniz.

Enjeksiyon Desenleri

Şuana kadar Constructer Injection yöntemiyle Ninject’i sizlere anlattım. Bunların dışında Property Setter Injection ve Setter Method Injection yöntemlerini de Ninject desteklemektedir.

Property Setter Injection

Ninject’in bağımlılıkları enjekte etmek istediğimiz propertyleri Inject attribute’u ile işaretlememiz yeterlidir.

    class Vasita
    {
        [Inject]
        public ITasit _tasit { get; set; }

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

Setter Method Injection

Property Setter Injection’a benzer şekilde gene Inject attribute’unu kullanmamız yeterlidir.

    class Vasita
    {
        ITasit _tasit;
        [Inject]
        public void VasitaAl(ITasit tasit) {
            this._tasit = tasit;
        }

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

Uygulamada kullanılan sınıflar arasında bağımlılık arttıkça Ninject kütüphanesinin değeri ve kıymeti daha çok anlaşılmaktadır.

Velhasıl, okuduğunuz için teşekkür ederim…

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

İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

Bir cevap yazın

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

*