8.08.2017

C# ile Dependency Injection ve Loosely Coupling

Düzgün ve detayli bir analiz yapilmadan gelistirme sürecine geçilmis bir çok projenin eninde sonunda basvurdugu nadide yöntemlerden biridir Dependency Injection.

Genellikle gelistirme sürecinde ihtiyaç duyulan gereksinimlere göre yapilan projelerde bir sorun görülmese de, zamanla ihtiyaçlarin artacagi ve siki baglitipler ile gelistirilmis projelerde ciddi sikintilar yasanabilmektedir.

Az önce güzel bir anahtar kelimeden bahsetti; siki bagli!
Aslinda biz bu anahtar kelimeden ziyade, bunun tam tersi ile ilgileniyor olacagiz. Yani; gevsek bagli (Loosely Coupled).

Gevsek bagli tipler ile çalismanin en büyük faydasi projenin ilerleyen zamanlarda gelisime açik ve rahatça revize edilebilmesidir.

Konuyu ilk hakimiyeti saglamanin en güzel yolu kanimca göresellerdir. Bunun için yandaki gibi fareye bagimli olmus bir insan elini örnek alabiliriz smiley

Günlük Hayattan Bir Senaryo

Bir proje yapiyor olalim. Bu projenin bir asamasinda da kullanicinin fareyi ara bir tip ile kullanmasi gerektiginiz düsünelim.

Proje tahmin edilecegi üzere düzgün bir sekilde analiz edilmedigi için farenin hiç bozulmayacagi ya da fareden beklenen ihtiyaçlarin hiç degismeyecegi düsünüldü. Bu nedenle de sokakta görülen ilk bilgisayarcidan bir fare alindi ve kullanicinin eline tipki resimde oldugu gibi verildi smiley

Bir müddet sonra fark ettik ki elimizdeki fare artik bizim ihtiyaçlarimizi karsilayamiyor. Bu durumda akla gelen ilk çözüm o fareyi atip, yerine yeni bir fare almak olacaktir. Tabi ki yazilim gelistirme ve revize islemlerindeki süreçler ne yazik ki günlük hayattan verdigimiz bu örnekteki gibi olmuyor blush

Senaryonun Yazilim Tarafi

Simdi ise düsündügümüz bu senaryoyu C# ile uygulamaya dökelim. Bunun için LogitechMouse adli bir sinif tasarliyor ve içerisinde ekrana kullanilan farenin markasini yazacak Write adli bir metod yaziyorum.

source code

1

2

3

4

5

6

7

class LogitechMouse

    {

        public void Write()

        {

            Console.WriteLine("Logitech Mouse Kullanildi");

        }

    }

Bir de mouse ile kullanici arasindaki entegrasyonu saglayacak ara bir sinif daha yaziyorum. Bu sinifimizin adi da MouseManager seklinde olsun ve içerisindeki Use metodu tetiklendigin de bir adet LogitechMouse sinifindan instance alsin ve Write metodunu tetiklesin.

source code

1

2

3

4

5

6

7

8

class MouseManager

    {

        public void Use()

        {

            LogitechMouse mouse = new LogitechMouse();

            mouse.Write();

        }

    }

Simdide Console uygulamamizin Main metodunda MouseManager sinifindan bir instance alalim ve Use metodunu tetikleyerek, LogitechMouse ile islemimizin yapilmasini saglayalim.

source code

1

2

3

4

5

6

7

8

class Program

    {

        static void Main(string[] args)

        {

            MouseManager manager = new MouseManager();

            manager.Use();

        }

    }

Su anda hersey harika gözüküyor fakat yazdigimiz kodun resmedilmis hali biraz yukarida sag tarafta bulunuyor smiley Uzun lafin kisasi her ne kadar su anda isimizi gören bir kod yazmis olsakta ilerleyen zamanlarda basimiz agriyabilir.

Gelin biraz basimizi agirtalim. LogitechMouse ne yazik ki gelisen ve degisen dünyamiza ayak uyduramadi ve bizim için artik yetersiz konuma düstü. Artik yeni bir fare ihtiyacimiz var. Biraz pazar arastirmasi yapiyor ve A4Tech marka bir fare satin aliyoruz. 

Yeni bir fare aldigimiz için bunuda haliyle yazilim süreçleri içerisinde tasarlamamiz gerekmektedir. Bunun içinde asagidaki gibi bir A4TechMouse adli sinif yazalim.

source code

1

2

3

4

5

6

7

class A4TechMouse

    {

        public void Write()

        {

            Console.WriteLine("A4Tech Mouse Kullanildi");

        }

    }

Bu kisimda basimizi agirtacak herhangi birsey olmadi ancak bu fareyi kullanabilmek için, elimizdeki fareyi kullanmamizi saglayan MouseManager adli sinifta ciddi bir degisiklik yapmamiz gerekmektedir!

Bu durumda LogitechMouse sinifindan aldigimiz instancelari iptal edip, yerine yeni tasarladigimiz A4TechMouse adli siniftan instancelar almaliyiz.

source code

1

2

3

4

5

6

7

8

9

class MouseManager

    {

        public void Use()

        {

            //LogitechMouse mouse = new LogitechMouse();

            A4TechMouse mouse = new A4TechMouse();

            mouse.Write();

        }

    }

Her bir fare degistirmek istedigimiz de ya da örnegimizden disari çikacak olursak, siki bagli tipler üzerine kurdugumuz bir projede her bir yapmak istedigimiz yenilikde yazdigimiz onlarca kod heba olacak hatta büyük projelerden bahsediyorsak durum, içinden çikilmayacak bir hal alacaktir.

Dependency Injection Yardimiyla Iyilestirmeler

Iste bu durumda siki bagli tipleri, Dependency Injetion ile birer gevsek bagli tip haline getirmemiz gerekmekte. Dikkat edilirse is katmani olarak nitelendirebilecegimiz MouseManager adli sinif hangi fareyi kullanacaksak, o fareye ait tipin Write metodunun tetiklemektedir. Bin çesit faremiz de olsa kullanacagimiz sadece bir metod oldugu için fareleri baz alacak bir soyut tip üretmemiz, çözümümüzün ilk adimi olacaktir.

Bu soyutlamayi abstract siniflar ile yapabilecegimiz gibi, interface'ler ile de yapabilir ki genellikle tercihimiz de bu yönde olmakta.

O zaman tüm farelerin implemente edecegi, asagidaki gibi bir interface yazalim;

source code

1

2

3

4

interface IMouse

    {

        void Write();

    }

Kullandigimiz fare tipleri için tasarladigimiz siniflarimiz hali hazirda zaten mevcut. Tek yapmamiz gereken tüm mouse tiplerinin IMouse adli interface'i implemente etmeleri yani her bir farenin artik birer IMouse tipinde olmasi olacaktir.

Bu durumda tasarladigimiz LogitechMouse ve A4TechMouse siniflari asagidaki hali alacaktir;

source code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

class LogitechMouse : IMouse

    {

        public void Write()

        {

            Console.WriteLine("Logitech Mouse Kullanildi");

        }

    }

 

    class A4TechMouse : IMouse

    {

        public void Write()

        {

            Console.WriteLine("A4Tech Mouse Kullanildi");

        }

    }

Entegrasyonu saglayan is katmanimizda ise artik bir instance alma islemi gerçeklestirmeyecegiz! Bunun yerine bir üst katmandan gelecek instance'in Write metodu tetiklenmelidir.

Ancak bu katmana hangi farenin gelecegi belli degil. Ancak az önce yaptigimiz iyilestirmeler sonucunda IMouse adli bir ana (base) tip elde ettik. Bu nedenle MouseManager sinifimiz asagidaki hali alabilir;

source code

1

2

3

4

5

6

7

8

9

10

11

12

class MouseManager

    {

        private readonly IMouse iMouse = null;

        public MouseManager(IMouse iMouse)

        {

            this.iMouse = iMouse;

        }

        public void Use()

        {

            iMouse.Write();

        }

    }

Mutlak suretle artik is katmanimiz kullanilmak istenilen farenin kurucu metod araciligi ile iletilmesini istemektedir. Bu durumda her iki fareyide kullanmamizi saglayan Main metodumuz asagidaki gibi olacaktir;

source code

1

2

3

4

5

6

7

8

9

class Program

    {

        static void Main(string[] args)

        {

            new MouseManager(new LogitechMouse()).Use();

 

            new MouseManager(new A4TechMouse()).Use();

        }

    }

Izledigimiz ve uyguladigimiz bu tasarim ile artik milyonlarca faremiz dahi olsa projemizde özelliklede is katmaninda hiç bir degisiklik yapmadan yeni tiplerimizi aynen kullanabiliriz.

Yaklasik 3 ayri makaleden olusacak bir yazi dizisinin baslangicini bugün yapmis olduk. Sonraki konularda IoC ve bu formu saglayan bazi frameworklerden bahsediyor olacagim.