Dependency Inversion (Bağımlılıkları Tersine Çevirme)

Derinlere dalmadan önce özetle bu prensip ile nesneler içerisinde başka bir nesne oluşturulmamasına özen gösterilmesi ve eğer bir nesneye ihtiyacımız var ise mümkünse dışarıdan alınması gerektiğini kavrıyor olacağız. Bu cümle sonrasındaki tüm yazılanlar bu prensipin ortaya çıkma nedenleri, benimsenmesi halinde bize kazandıracakları ve örnek uygulaması ile ilgili olacak.

Uygulamalarımız içerisinde sürekli olarak yeni nesnelere ihtiyaç duyuyor ve haliyle oluşturuyoruz. Bu nesneleri oluştururken ve kullanırken herhangi bir prensip gözetmezsek o uygulamayı sancılı günler bekliyordur ve farkında olmasakta teknik borçlanmalara neden oluruz. Oluşturduğumuz bu nesnelerin sayısı arttıkça istem dışı düşük bağımlılık (loose coupling) prensibine aykırı hareket etmeye başlarız. İşte tam bu karmaşıklığı engellemek ve düşük bağımlılığa sahip nesneler oluşturabilmek için bu prensipi benimseyip, kodlarımıza yansıtmalıyız.

Ayrıca bu prensip test edilebilir kod yazmanızı, Mock sınıflar oluşturabilmenizi sağlar ki bunun göz ardı edilemeyecek bir fayda olduğunu özellikle belirtmek isterim.

Bu prensip ile nesne oluşumu ve yönetimini merkezileştiriyoruz, nesneler içerisinde yeni nesneler oluşturmuyor, eğer ihtiyacımız var ise mümkünse dışarıdan alıyoruz diyoruz peki ama neden?

Düşünelim, Car isminde bir sınıfımız var ve constructor parametrelerine yeni bir parametre eklememiz gerekti; bu classtan yeni instance oluşturduğumuz her yeri gidip bu parametre nedeniyle güncellememiz gerekiyor. Bu prensipe uygun geliştirme yaptıysak bu classın new edildiği çok az (hatta mümkünse tek bir) yer olması gerekiyor ki böyle bir değişiklik halinde de yalnızca gidip o noktayı güncelleyip hızlı şekilde uygulamamızı geliştirip yeni özellikler kazandırabiliriz.

Bir tık daha ileri gidelim mi?
Elimizdeki şu anda 1 adet araba olabilir fakat ya 2. araç gelirse? Gelecekte 2. araç geldiğinde karın ağrıları yaşamak istemiyorsak; markalara göre araç yetenekleri değişebileceğinden bu sınıfı soyutlamamız (abstraction uygulamamız) gerekiyor. Farkındaysanız gittikçe bağımlılıklarımızı azaltıyoruz Bunun için ICar adında bir interface oluşturup sonrasında Porche, Wolkswagen, Volvo gibi sınıfları ICar interface’i implement ederek yolumuza karın ağrıları yaşamadan devam ediyoruz.

Şimdi birazda kod üzerinden anlayalım;

var badSample = new BadSample();
Console.WriteLine(badSample.GetNumberOfTires());

BadSample ile elimizde taşıt olarak yalnızca Bisiklet olduğunu düşünerek kod yazdık ve bu kötü gelecekte başka bir taşıt geldiğinde if-else/switch blokları bizi bekliyor gibi görünüyor.

var vehicle = new Bicycle();
var goodSample = new GoodSample(vehicle);
Console.WriteLine(goodSample.GetNumberOfTires());

GoodSample incelediğimizde bugün elimizde bisiklet varken;

var vehicle = new Car();
var goodSample = new GoodSample(vehicle);
Console.WriteLine(goodSample.GetNumberOfTires());

Araba geldiğinde hiç sorun yaşamadan ve GoodSample sınıfı içerisinde bir güncelleme yapmadan SOLID prensiplerinden bu incelemiş olduğumuz prensipe ek olarak Open/Closed Principle’a uygun olarak kod yazdığımızı görüyoruz.

Dependency Inversion prensibinin en bilinen uygulamaları Inversion of Control’dür.

Özetle bu prensip yanında bir çok bonus ile geliyor, dikkate alın güzeldir 🙂