5 yaşındaki bir çocuğa dependency injection ‘ı anlatmanız istense nasıl anlatırdınız? Benim bunu açıklamam biraz zor olurdu. Ama stackoverflow’da verilen cevap gerçekten mükemmel.
5 yaşında bir çocuk birşeyler almak için buzdolabına gider ise bazı problemlere neden olabilir. Buzdolabının kapağı açık kalabilir veya ebeveynlerinin istemediği şeyleri alabilir. Hatta buzdolabının dışındaki şeylere bakabilir veya buzdolabından tarihi geçmiş birşeyi alabilir. Çocuğun asıl yapması gereken “Öğle yemeğim ile bir içeceğe ihtiyacım var” gibi bir istekte bulunmaktır. Çocuk böyle bir istekte bulunduğunda yemeğini alır ve yer. (Nasıl, nereden alıcam diye düşünmez, istemesi yeter :))
Dependency Injection Frameworklerinin de mantığı budur. Eğer bir nesneye ihtiyacın olduğunda bunu sen oluşturmazsın. Bu nesneye ihtiyacın olduğunu söylersin. Böylece framework o nesneyi oluşturur ve sana getirir. Ve bu nesnenin yaşam döngüsünden framework sorumludur. Böylece sen sadece neye ihtiyacım olduğunu söyler ve o nesneyi kullanırsın.
Dependency’ler nelerdir:
– kullandığımız framework
– 3rd party kütüphaneler
– database
– file system
– email server
– servisler
– sistem kaynakları ( saat, tarih vs. )
– configuration
– new keyword’ü ile oluşturulan nesneler
– static metotlar
burada framework’ü stable dependency olarak değerlendiriyoruz çünkü uygulamamınızın modülerliğini engellemiyor. Base class library her zaman için kullanıma açık çünkü uygulamanın çalışması için şart. bunları stable dependency olarak değerlendirmemizin nedeni bizim için yazılmış, test edilmiş olmaları, deterministik davranış göstermeleri, yerlerine başka class’lar yerleştirmeyi düşünmememiz. kullandığımız kütüphaneler de çoğu zaman(kendisi stable ise tabi) stable dependency olarak görülür.
öte yandan listedeki diğer tüm öğeler volatile dependency olarak sınıflandırılır. bu dependency’lerin davranışları hiçbir zaman consumer nesne tarafından bilinemez bu yüzden nesnenin test edilebilirliğini engeller.
örneğin saat başı bir işlem yapan bir sınıfın çalıştığını nasıl test edebiliriz? testi saatlerce çalıştırmak pek iyi bir çözüm olmaz. peki sistem saatini öğrenme işlemini basit bir abstraction arkasına alırsak? consumer sadece bu abstraction’ı bilirse test içinde bunu bir test double ile değiştirmek çok kolay.
böylece database, file system gibi dependency’leri aynı yöntemle yönetmek, her test için istediğimiz durumu yaratmak bizim elimizde. Bir diğer önemli nokta bu dependency’lerin new keyword’ü ile oluşturulmaması. new keyword’ü hem abstraction’ı implemente eden concrete class’a ayrı bir dependency yaratıyor, hem de bir test double ile değiştirmeyi imkansız kılıyor(public olarak kullanmak istemediğimizi varsayarsak).
Bu noktada constructor injection yardımımıza koşuyor. consumer, constructor’ında ihtiyaç duyduğu tüm dependency’leri parametre olarak belirtiyor. Artık bu nesnenin çalışabilmesi için hangi dependency’lere ihtiyacı olduğunu açıkça görebiliyoruz. Belirtilen tüm parametreler concrete class yerine abstraction olduğu için bunları test double’ları ile değiştirmek artık mümkün, class new keyword’ü ile dependency’yi oluşturduğunda bu mümkün değildi. peki uygulama üzerinde concrete class’ları bu sınıfa inject etmek kimin işi? cevap tabi ki DI Container (Dagger)