Zayıf bağlı yazılımlar üretmek: Bağımlılıkların eklenmesi


Yazıma başlamadan önce anlatacağım konunun teknik bir konu olduğunu sizinle paylaşmak istiyorum.

Bu yazımda size, zayıf bağlı yazılımlar üretmek ve bu konunun önemi hakkında bilgi vereceğim. Yazılım geliştirme de diğer mühendislik disiplinlerinde olduğu gibi bazı prensiplerin üzerine inşa edilmesi gereken bir süreçtir. Bu prensiplerin en önemlilerinden biri ise yazılımımızın parçalarının birbirine olan bağlarının zayıf olmasıdır.

Bağımlılıkların zayıflatılması işleminin nasıl yapılacağı hakkında bilgi vermeye başlamadan önce, “Bunu neden yapmalıyız?” sorusunu cevaplamayı önemli görüyorum.

Bir A sınıfımızın B1 ve B2 gibi farklı sınıflarda kullanıldığını varsayalım. Bu durumda;

  • A sınıfını farklı bir implementasyonla değiştirmemiz durumunda B1 ve B2 sınıflarında da değişiklik gerekmeli,
  • Eğer A sınıfının bir bağımlılığı mevcutsa bu bağımlılıklar B1 ve B2 sınıfları tarafından da konfigüre edilmeli,
  • Ayrıca A sınıfının mocklanması mümkün olmadığı için B1 ve B2 sınıfının birim testi implementasyonu farklı zorluklarla karşılaşır hale geliyor.  

Konuya ilişkin nedeni konuştuğumuza göre artık nasılın üzerinde durabiliriz. Örneğimizde B sınıflarının A sınıfının constructor’ına erişmesini önleyecek yöntem kritik düzeyde gerekli. Bu konuda ihtiyacımız olan A’nın nasıl üretileceğini bilen bir yapının oluşturulması. Bu noktada, A’nın instance’ı değil nasıl oluşturulacağını tutuyor olmamız önemli çünkü konu A’nın yani servislerin lifetime’ıyla alakalı.

Servis Yaşam Süreleri

Geliştirdiğimiz yazılıma ait servisleri üç farklı şekilde kaydedebiliriz:

Transient

Transient lifetime’a sahip servisler, yukarıda bahsettiğim mekanizma (DI Container) tarafından her talep edildiğinde yeniden üretilip ilgili kaynağa paylaşımı yapılmaktadır. Lightweight, statesiz servislerin kullanımı için önerilen servis yaşam süresidir.

Scoped

Scoped lifetime’a sahip servisler, web uygulamalarda client’ın her bir request’i (connection) için bir kez üretilen servisler anlamına gelir. Scoped servisler request’in yaşam döngüsü tamamlandığı vakit ömürlerini doldururlar. Transient’ten farklı olarak client request’i bir response’a dönüşünceye kadar aynı servisi birden fazla kez de talep etseniz DI container’ı size servisin aynı instance’ını verecektir. Buna örnek olarak database connection’ları için bu yaşam süresinin uygun olacağını düşünebiliriz.

Singleton

Servis her talep edildiğinde aynı instance’a erişim bekleniyorsa seçilmesi gereken yaşam süresi budur. Bu instance ya ilgili servis ilk talep edildiğinde ya da developer tarafından özel olarak üretildiğinde (inject ederken) üretilir.

Belirtmek istediğim önemli bir not ise scoped lifetime’lı bir servisin singleton bir serviste çözümlemesi servisinizin istenmeyen state’lerde bulunmasıyla sonuçlanabilmesidir. Burada beklenen

  • Singleton servisin scoped veya transient serviste,
  • Scoped servisin başka bir scoped serviste veya transientte çözümlenmesidir.

Tüm bu bilgiler ışığında, zayıf bağlı yazılım üretmenin, bize SOLID’in birbiriyle benzer şeyleri resmeden iki prensibinin sağlanmasında katkı sağlayacağını söylemek mümkündür. Bu prensipler aşağıdaki gibidir:

  • Dependency Inversion prensibi der ki; bir servis ancak soyutlamalara (interface) bağımlı olmalı, concrete class’lara değil.
  • Single Responsibility prensibi der ki; yazılımın spesifikasyonundaki olası tek bir değişiklik bir servisin değişmesine etmen olabilmelidir.
PAYLAŞ:

Günümüzde başarılı olmak için neden dijital inovasyon önceliklendirilmeli?

Başarılı satın alma teknolojisi uygulamalarının yapıtaşları