Anıl Şenocak

Dependency injection, bir uygulamadaki sınıflar arasında gevşek bağlantı(loose coupling) uygulamaya yönelik bir yaklaşımdır. Bağımlılıkları enjekte etmenin farklı yolları vardır ve bu makale neden yapıcı enjeksiyonunun tercih edilen yol olması gerektiğini açıklamaktadır.
Dependency: Bir nesne, işlemlerini gerçekleştirmek için genellikle diğer sınıfların nesnelerine ihtiyaç duyar. Bu nesnelere bağımlılıklar diyoruz.
Injection: Bir nesneye gerekli bağımlılıkları sağlama süreci.
Bu nedenle dependency injection, kontrolün tersine çevrilmesinin (inversion of control, IoC) uygulanmasına yardımcı olur. Bu, bağımlılık nesnelerini oluşturan sınıf yerine, nesne oluşturma ve bağımlılıkları enjekte etme sorumluluğunun framework'e (yani Spring) verildiği anlamına gelir.
Aşağıdakilerle bağımlılık enjeksiyonu uygulayabiliriz:
Sınıf için gereken bağımlılıklar yapıcıya bağımsız değişkenler olarak sağlanır:
@Component
class Cake {
private Flavor flavor;
Cake(Flavor flavor) {
Objects.requireNonNull(flavor);
this.flavor = flavor;
}
Flavor getFlavor() {
return flavor;
}
//...
}
Spring 4.3'ten önce, constructor'a bir @Autowired ek açıklaması eklememiz gerekiyordu. Daha yeni sürümlerde, sınıfın yalnızca bir yapıcı metodu varsa bu isteğe bağlıdır. Yukarıdaki Cake sınıfında, yalnızca bir kurucumuz olduğundan, @Autowired açıklamasını belirtmemize gerek yoktur. Aşağıdaki iki kurucu örneğini düşünün:
@Component
class Sandwich {
private Topping toppings;
private Bread breadType;
Sandwich(Topping toppings) {
this.toppings = toppings;
}
@Autowired
Sandwich(Topping toppings, Bread breadType) {
this.toppings = toppings;
this.breadType = breadType;
}
//...
}
Birden çok yapıcı metoda sahip bir sınıfımız olduğunda, @Autowired açıklamasını herhangi bir kurucuya açıkça eklememiz gerekir, böylece Spring, bağımlılıkları enjekte etmek için hangi kurucunun kullanılacağını bilir.
Gerekli bağımlılıkları sınıfa alan parametreleri olarak injeckte ediyoruz ve değerler, özelliklerin "setter" yöntemleri kullanılarak ayarlanıyor. Setter metodlarını @Autowired açıklama ile açıklamalıyız. Cake sınıfı, Topping türünde bir nesne gerektirir. Topping nesnesi, bu özelliğin ayarlayıcı yönteminde bir argüman olarak sağlanır:
@Component
class Cookie {
private Topping toppings;
@Autowired
void setTopping(Topping toppings) {
this.toppings = toppings;
}
Topping getTopping() {
return toppings;
}
//...
}
Spring, @Autowired açıklamasını bulacak ve ayarlayıcıyı bağımlılığı enjekte etmesi için çağıracaktır.
Spring, gerekli bağımlılıkları doğrudan @Autowired açıklama ile açıklama ekleme alanlarına atar. Bu örnekte, Spring'in Topping bağımlılığını alan enjeksiyonu yoluyla enjekte etmesine izin veriyoruz:
@Component
class IceCream {
@Autowired
private Topping toppings;
Topping getToppings() {
return toppings;
}
void setToppings(Topping toppings) {
this.toppings = toppings;
}
}
@Autowired'ı her ikisine de bir alan ve bir ayarlayıcı eklersek ne olur? Spring, bağımlılığı enjekte etmek için hangi yöntemi kullanacak?
@Component
class Pizza {
@Autowired
private Topping toppings;
Topping getToppings() {
return toppings;
}
@Autowired
void setToppings(Topping toppings) {
this.toppings = toppings;
}
}
Yukarıdaki örnekte, hem "set" fonksiyonuna hem de alana @Autowired açıklamasını ekledik. Bu durumda Spring, setter enjeksiyon yöntemini kullanarak bağımlılık enjekte eder. Kodu daha az okunabilir hale getireceğinden enjeksiyon türlerini tek bir sınıfta karıştırmanın kötü bir uygulama olduğunu unutmayın.
Artık farklı enjeksiyon türlerini gördüğümüze göre, yapıcı enjeksiyon kullanmanın bazı avantajlarından geçelim.
Objeyi yapıcı metod çağrıdığında oluştururuz. Yapıcı metod gerekli tüm bağımlılıkları parametre olarak beklerse, o zaman sınıfın bağımlılıkları enjekte edilmeden asla somutlaştırılmayacağından %100 emin olabiliriz.
IoC konteyneri, yapıcı metoda sağlanan tüm argümanların yapıcıya aktarılmadan önce kullanılabilir olmasını sağlar. Bu, kötü şöhretli NullPointerException'ı önlemeye yardımcı olur.
Yapıcı enjeksiyonu son derece kullanışlıdır, çünkü gerekli tüm bağımlılıkların yüklenip yüklenmediğini kontrol etmek için her yerde ayrı bir iş mantığı yazmak zorunda olmadığımız için kod karmaşıklığı basitleştirilir.
İsteğe Bağlı Bağımlılıklar Ne Olacak? Ayarlayıcı(setter) enjeksiyonu ile Spring, bir ayarlayıcı yöntemine @Autowired(required=false) ekleyerek isteğe bağlı bağımlılıkları belirlememizi sağlar. required=false tüm yapıcı bağımsız değişkenlerine uygulanacağından bu, yapıcı ekleme ile mümkün değildir.
Java'nın Optional türünü kullanarak yapıcı enjeksiyonu ile isteğe bağlı bağımlılıklar sağlamaya devam edebiliriz.
Constructor Injection, beanlerimizin çok fazla başka nesneye bağımlı olup olmadığını belirlememize yardımcı olur. Kurucumuzun çok sayıda argümanı varsa, bu sınıfımızın çok fazla sorumluluğu olduğunun bir işareti olabilir. Sorunların uygun şekilde ayrılmasını daha iyi ele almak için kodumuzu yeniden düzenlemeyi düşünmek isteyebiliriz.
Yapıcı enjeksiyonu, birim testleri yazmayı basitleştirir. Yapıcı, bizi tüm bağımlılıklar için geçerli nesneler sağlamaya zorlar. Mockito gibi mocking kütüphaneleri kullanarak, daha sonra kurucuya aktarabileceğimiz sahte nesneler oluşturabiliriz.
Elbette ayarlayıcılar(setter) aracılığıyla da mock dataları verebiliriz, ancak bir sınıfa yeni bir bağımlılık eklersek, testte ayarlayıcıyı çağırmayı unutabiliriz, bu da potansiyel olarak testte bir NullPointerException'a neden olur. Yapıcı enjeksiyonu, test senaryolarımızın yalnızca tüm bağımlılıklar mevcut olduğunda yürütülmesini sağlar.
Yapıcı enjeksiyonu, sabit nesneler oluşturmaya yardımcı olur çünkü bir yapıcının imzası, nesneleri oluşturmanın tek olası yoludur. Bir bean oluşturduğumuzda, artık bağımlılıklarını değiştiremeyiz. Ayarlayıcı enjeksiyonu ile, oluşturulduktan sonra bağımlılığı enjekte etmek mümkündür, bu da diğer şeylerin yanı sıra çok iş parçacıklı bir ortamda iş parçacığı güvenli olmayabilen ve değişkenliklerinden dolayı hata ayıklaması daha zor olan değişken nesnelere yol açar.
Constructor injection, kodu daha sağlam hale getirir. NullPointerException'ı ve diğer hataları önleyerek değişmez nesneler oluşturmamızı sağlar.