27 Ocak 2022 Perşembe

Dependency Injection ve Dependency Inversion - PYTHON

erhaba,

Bugünkü yazımda Dependency Injection ve Dependency Inversion konularını inceleyip python ile örnek kodlar yazmaya çalışacağım.

Birçok kaynakta karışık bir şekilde anlatılan bu yapıları bakalım herkesin anlayabileceği bir şekle sokabilecek miyiz...

Dependency Inversion konsepti yazılımda S.O.L.I.D prensiplerinde tanıtılmaktadır ve bu kelimenin son harfi olan D ile gösterilmektedir. Dependency Injection ise Dependency Inversion uygulanma tekniklerinden biri olarak tanımlanabilir. Amaç diğer sınıflara bağımlılığı en aza indirecek bir yapı kurmaktır.



Bir memeli sınıfımız olsun. Bu sınıf içinde başka bir hayvan sınıfını kullanıyorsak, burda bir bağımlıktan söz etmek mümkündür. Yani memeli sınıfı hayvan sınıfına bağımlıdır. Bu bağımlılığı azaltabilmek için öncelikle dependency injection uygulanması gerekir. Alttaki kodlarımızda kısaca şunu yapacağız. Hayvan sınıfını da kullanarak bir memeli oluşturacağız. Memeli sınıfından oluşturulan nesne ye metodu aracılığı ile komut verip yürüteceğiz ve durduracağız.

class hayvan():
def yuru(self):
print("Yuru...Yuruyor.")

def dur(self):
print("Dur!...Durdu.")

class memeli():
def __init__(self):
self.hayvan = hayvan()
self.durum = False

def komut_ver(self):
if self.durum:
self.hayvan.yuru()
self.durum = False
else:
self.hayvan.dur()
self.durum = True

kedi = memeli()
kedi.komut_ver()
kedi.komut_ver()
kedi.komut_ver()
kedi.komut_ver()
kedi.komut_ver()
Çıktı :

Dur!...Durdu.
Yuru...Yuruyor.
Dur!...Durdu.
Yuru...Yuruyor.
Dur!...Durdu.

Dependency Injection bağımlılıkların dışardan alınması şeklinde sağlanmaktadır. Şimdi dependency injection ile üstteki kodumuzu alttaki şekilde düzenleyelim.

 
class hayvan():
def yuru(self):
print("Yuru...Yuruyor.")

def dur(self):
print("Dur!...Durdu.")

class memeli():
def __init__(self, memeliHayvan: hayvan):
self.memeliHayvan = memeliHayvan
self.durum = False

def komut_ver(self):
if self.durum:
self.memeliHayvan.yuru()
self.durum = False
else:
self.memeliHayvan.dur()
self.durum = True

kedi = hayvan()
sonic = memeli(kedi)

sonic.komut_ver()
sonic.komut_ver()
sonic.komut_ver()
sonic.komut_ver()
sonic.komut_ver()
Çıktı :

Dur!...Durdu.
Yuru...Yuruyor.
Dur!...Durdu.
Yuru...Yuruyor.
Dur!...Durdu.

Kodun yeni halini incelersek hayvan sınıfından üretilen nesne memeli sınıfına dışarıdan verilmektedir. Yani memeli sınıfı içinde hayvan sınıfı kullanılarak bir nesne üretimi yapılmamaktadır. Bu bağımlılığı dependency injection ile kaldırmış olduk.

Peki işi biraz ileriye götürürsek, memeli sınıfına gönderilecek hayvanları iki ayaklı ve dört ayaklı olarak ikiye ayırıp bunlar için birer ara sınıf oluşturmak istersek bunu nasıl yöneteceğiz. Burada iki ekstra sınıftan bahsediyoruz ama onlarca sınıf olabilir. Peki iki ayaklı ve dört ayaklı şeklinde ayarlayacağımız iki sınıftan üretilen hayvanları memeli sınıfına gönderebilir miyiz ? Yukarıdaki örnekte hayvan sınıfından üretilen hayvan nesnelerini memeli sınıfına gönderecek şekilde ayarlamalarımızı yaptık. Peki iki ayaklı hayvan nesnesi ve dört ayaklı hayvan sınıf nesnelerini memeli sınıfına nasıl gönderebiliriz ? Bunu direkt olarak yapmamız mümkün değildir, yani bu nesneler memeli sınıfı tarafından algılanamayacaktır. Çünkü memeli sınıfı hayvan sınıfı nesnelerini algılamak üzere geliştirildi.

Bunu interface kullanımı yardımı ile çözeriz. iki ayaklı hayvan ve dört ayaklı hayvan sınıfları bu interface'den implement alırsa sorunumuz çözülecektir. Bu durumda hayvan sınıfımız bir interface olacak. Memeli sınıfı hayvan sınıfından nesneleri kabul edecek şekilde ayarlanacak. İki ayaklı hayvan ve dört ayaklı hayvan sınıfları bu interface'den implement alarak bunlardan üretilen nesneler memeli sınıfına gönderilebilecek.

Şimdi bu senaryoya göre dependency inversion uygulayalım.

Tekrar özetlersek. Hayvan isimli bir interface olacak. İki ayaklı hayvan ve Dört ayaklı hayvan isimli birer sınıf bu interfaceden implement alacak ve bu sınıflardan üretilecek nesneler memeli sınıfına gönderilerek memeli metodu olan komut_ver() çağırılacak.


from abc import ABC, abstractmethod

class hayvan(ABC):
@abstractmethod
def yuru(self):
pass

@abstractmethod
def dur(self):
pass

class iki_ayakli_hayvan(hayvan):
def yuru(self):
print("İki Ayaklı Yuru...Yuruyor.")

def dur(self):
print("İki Ayaklı Dur!...Durdu.")

class dort_ayakli_hayvan(hayvan):
def yuru(self):
print("Dort Ayaklı Yuru...Yuruyor.")

def dur(self):
print("Dort Ayaklı Dur!...Durdu.")

class memeli():
def __init__(self, memeliHayvan: hayvan):
self.memeliHayvan = memeliHayvan
self.durum = False

def komut_ver(self):
if self.durum:
self.memeliHayvan.yuru()
self.durum = False
else:
self.memeliHayvan.dur()
self.durum = True

kedi = dort_ayakli_hayvan()
sonic = memeli(kedi)

maymun = iki_ayakli_hayvan()
jimny = memeli(maymun)

jimny.komut_ver()
jimny.komut_ver()
jimny.komut_ver()
print()
sonic.komut_ver()
sonic.komut_ver()
sonic.komut_ver()
Çıktımız :

İki Ayaklı Dur!...Durdu.
İki Ayaklı Yuru...Yuruyor.
İki Ayaklı Dur!...Durdu.

Dort Ayaklı Dur!...Durdu.
Dort Ayaklı Yuru...Yuruyor.
Dort Ayaklı Dur!...Durdu.

Evet böylece ilk kodumuza önce dependency injection uygulayarak sınıf bağımlılığından kurtardık. Sonrasında ise daha detaylı bir sınıf yapısı kurarak sınıf çeşitliliğimizi arttırma amacı ile kodumuzu bir interface den implement ederek inşaa ettik. Bu sayede dependency inversion uygulamış olduk. Bunun bir avantajı da artık aynı interfaceden implement eden yeni farklı ara sınıfları kodumuza eklemek ve memeli sınıfına göndermek sureti ile yeni tür nesneler yaratabileceğiz. Örneğin kırkayak adında üçüncü bir sınıfı hızlıca kodumuza ekleyebiliriz. Hemde interface ve memeli sınıflarını değiştirmeden.

Bunu farklı bir örnek tasarımı ile açıklamaya çalışalım. Bir adet Tır sınıfımız olsun, bir adet Uçak sınıfımız olsun, bir adet Otobüs sınıfımız olsun. Bir kargo lojistik firması için yazılım geliştiriyor olalım. Bu 3 sınıfı bir adet Operasyon isimli interface sınıfından implement ederiz. Bu Operasyon sınıfının Taşı isimli bir metodu olacaktır. Dolayısı ile Uçak, Otobüs, Tır sınıflarının da Taşı isimli metodu olacaktır. Bu 3 sınıftan üretilen nesneleri ana sınıfımız olan Kargo sınıfına göndereceğiz.
Kısaca  Operasyon()  ->  ( Uçak() , Otobüs() , Tır() )  ->  Kargo() şeklinde bir yapı olacaktır.
Görebileceğiniz üzere böyle bir yapısı olan kargo firması ilerleyen yıllarda bir gemi alıp gemi kargosu işlerine girerse Gemi() sınıfını sisteme dahil etmek oldukça kolay olacaktır ve bu ekleme diğer sınıfları etkilemeyecektir.

Teşekkürler,
Cem Selmanoğulları 










Hiç yorum yok:

Yorum Gönder