DI Hakkında Sıkça Sorulan Sorular (FAQ)

DI, IoC'nin başka bir adı mı?

Inversion of Control (IoC), bir programdaki denetim akışını anlatan bir ilkedir: kodunuz mu dış kodu çağırıyor, yoksa dış kod (örneğin bir framework) mu sizin kodunuzu çağırıyor? IoC; olayları, “Hollywood ilkesi” olarak bilinen yaklaşımı (Hollywood Principle) ve başka yönleri kapsayan geniş bir kavramdır. Bu kavram, Kural #3: Bırakın factory halletsin bölümünde anlatılan ve new operatörünün tersine çevrilmesini temsil eden factory'leri de içerir.

Dependency Injection (DI) ise nesnelerin bağımlılıklarını (yani birlikte çalışmaları gereken diğer nesneleri) nasıl edindiğine odaklanır. Bağımlılıkları nesnelerin kendisinin oluşturması ya da bulması yerine, onlara açıkça aktarılmasını savunan bir tasarım desenidir.

Dolayısıyla DI, IoC'nin özel bir biçimi sayılabilir. Ancak IoC'nin her biçimi temiz kodu desteklemez. Örneğin genel duruma ya da Service Locator desenine dayanan teknikler birer anti-desendir.

Service Locator nedir?

Bağımlılık enjeksiyonuna alternatif bir yaklaşımdır. Kullanılabilir tüm servislerin (bağımlılıkların) kaydedildiği merkezi bir nesne (locator) içerir. Bir nesnenin bir bağımlılığa ihtiyacı olduğunda onu Service Locator'dan ister.

Ancak DI ile karşılaştırıldığında saydamlıktan yoksundur. Bağımlılıklar, nesnenin API'sinde (yapıcıda ya da metotlarda) açıkça görünmek yerine nesnenin kodunun içinde (locator çağrılarında) gizlidir; bağlantıları anlamak için kodu incelemek gerekir. Test etmek de daha karmaşıktır; nesneyi örneklerken sahte bağımlılıkları öylece aktaramazsınız, çoğu zaman Service Locator'ın kendisiyle uğraşmanız gerekir. Üstelik Service Locator gereksiz bir bağımlılık getirir: nesneler locator'a bağlanır; oysa DI'da nesneler ideal olarak container'dan habersiz kalır.

DI'yı ne zaman kullanmamak daha iyidir?

Bağımlılık enjeksiyonu tasarım deseninin doğru kullanımının bilinen kayda değer bir sakıncası yoktur. Tersine, bağımlılıkları her yerden erişilebilir yerlerden (statik özellikler ya da singleton'lar gibi) edinmek, tıpkı Service Locator kullanmak gibi pek çok soruna yol açar. Bu yüzden DI kullanmak genellikle her zaman önerilir. Bu bir dogma değildir; yalnızca bağımlılıkları temiz biçimde yönetmek için daha iyi bir alternatif yaygın olarak benimsenmemiştir.

Yine de nesnelere her yerden erişmenin kabul edilebilir olduğu belirli, sınırlı durumlar vardır. Örneğin hata ayıklama sırasında bir değişkenin değerini dökmeniz, yürütme süresini ölçmeniz ya da belirli bir noktada bir mesaj günlüklemeniz gerektiğinde. Sonradan koddan kaldırılacak geçici işlemleri kapsayan bu durumlarda, her yerden erişilebilir bir dumper, zamanlayıcı ya da logger kullanmak meşru olabilir. Bu araçlar uygulamanın temel tasarımının parçası değildir.

DI kullanmanın sakıncaları var mı?

Bağımlılık enjeksiyonu kullanmak, daha fazla kod yazma çabası ya da düşük başarım gibi sakıncalar getirir mi? DI'ya uygun kod yazmaya başladığımızda ne kaybederiz?

DI'nın kendisi, çalışma zamanı başarımına ya da bellek kullanımına neredeyse hiç etki etmez. DI container'ının başarımı bir etken olabilir, ama Nette DI container'ı düz PHP koduna derler; bu da uygulama çalışırken neredeyse sıfır ek yük demektir.

DI ilkelerine göre kod yazarken, çoğu zaman bağımlılıkları alan yapıcılar oluşturmanız gerekir. Bu geçmişte zahmetli görünmüş olabilir, ama modern IDE'ler ve PHP 8'in constructor property promotion gibi özellikleri bunu çok hızlı kılıyor. Factory'ler de çoğu zaman Nette DI tarafından otomatik üretilebilir; bu da tekrar eden kodu daha da azaltır. Buna karşılık singleton ve statik accessor yazma gereğini ortadan kaldırırsınız.

Genel olarak, DI kullanan iyi tasarlanmış bir uygulama, singleton'lara ya da genel erişime dayanan bir uygulamadan tipik olarak ne belirgin biçimde kısadır ne de uzun. Bağımlılıkların oluşturulması ve bağlanmasıyla ilgili kod, yalnızca tek tek sınıflardan bunun için ayrılmış yerlere taşınır: DI container yapılandırmasına ve factory'lere.

Eski bir uygulama DI'ya nasıl dönüştürülür?

Eski bir uygulamadan bağımlılık enjeksiyonuna geçmek, özellikle büyük ve karmaşık uygulamalarda zorlu bir süreç olabilir. Bu sürece dizgeli yaklaşmak önemlidir.

  • Bağımlılık enjeksiyonuna geçerken, kullanılan ilkeleri ve uygulamaları tüm ekip üyelerinin anlaması önemlidir.
  • Önce var olan uygulamayı çözümleyerek temel bileşenleri ve bağımlılıklarını belirleyin. Hangi bölümlerin hangi sırayla yeniden düzenleneceğine dair bir plan yapın.
  • Bir DI container gerçekleştirin ya da daha iyisi Nette DI gibi var olan bir kütüphaneyi kullanın.
  • Uygulamanın bölümlerini bağımlılık enjeksiyonu kullanacak şekilde kademeli olarak yeniden düzenleyin. Bu, yapıcıları ya da metotları bağımlılıkları parametre olarak alacak şekilde değiştirmeyi içerebilir.
  • Nesnelerin örneklendiği kodu, onları container'dan alacak ya da container'ın sunduğu factory'leri kullanacak şekilde güncelleyin.

Bağımlılık enjeksiyonuna geçişin, kod kalitesine ve uygulamanın uzun vadeli bakımına yapılan bir yatırım olduğunu unutmayın. Bu değişiklikleri yapmak zorlu olabilse de, sonuç; gelecekteki genişletmelere ve bakıma hazır, daha temiz, daha modüler ve kolay test edilebilir bir kod olmalıdır.

Kompozisyon neden kalıtıma yeğlenir?

Kodu yeniden kullanmada kompozisyon, daha gevşek bağlantıya yol açtığı için genellikle kalıtıma yeğlenir. Kompozisyonla, bir temel sınıfı değiştirmenin ona bağlı alt sınıfları bozması sorunuyla karşılaşma olasılığınız daha düşüktür. Tipik örnek, constructor hell denen durumdur.

Nette DI Container, Nette dışında kullanılabilir mi?

Kesinlikle. Nette DI Container, Nette'nin bir parçasıdır, ama framework'ün diğer bölümlerinden bağımsız kullanılabilen ayrı bir kütüphane olarak tasarlanmıştır. Composer ile kurmanız, servislerinizi tanımlayan bir yapılandırma dosyası oluşturmanız ve birkaç satır PHP koduyla DI container'ı yaratmanız yeter. Ardından projelerinizde bağımlılık enjeksiyonundan hemen yararlanmaya başlayabilirsiniz.

Nette DI Container bölümü, kod örnekleriyle somut bir kullanım durumunu anlatır.

Yapılandırma neden NEON dosyalarında?

NEON, uygulamaları, servisleri ve bağımlılıklarını ayarlamak için Nette içinde geliştirilmiş, basit ve kolay okunur bir yapılandırma dilidir. JSON ya da YAML ile karşılaştırıldığında, bu amaç için çok daha sezgisel ve esnek olanaklar sunar. NEON'da, JSON ya da YAML'de bu kadar anlaşılır biçimde ifade edilmesi zor ya da olanaksız olan servis tanımlarını ve ilişkileri doğal biçimde anlatabilirsiniz.

NEON dosyalarının ayrıştırılması uygulamayı yavaşlatır mı?

NEON dosyaları çok hızlı ayrıştırılsa da, ayrıştırma hızları üretimde büyük ölçüde önemsizdir. Bunun nedeni, yapılandırma dosyalarının yalnızca uygulama ilk çalıştığında (ya da değiştiklerinde) bir kez ayrıştırılmasıdır. Ayrıştırmadan sonra DI container kodu üretilir, önbelleğe alınır (diske yazılır) ve sonraki her istekte bu derlenmiş PHP kodu çalıştırılır; böylece yeniden ayrıştırmaya gerek kalmaz.

Üretim ortamında bu böyle işler. Geliştirme sırasında NEON dosyaları, içerikleri her değiştiğinde ayrıştırılır; böylece geliştiricinin elinde her zaman güncel bir DI container bulunur. Belirtildiği gibi, ayrıştırmanın kendisi çok hızlıdır.

Yapılandırma dosyasındaki parametrelere sınıfımdan nasıl erişirim?

Kural #1: Bırakın size aktarılsın ilkesini akılda tutun. Bir sınıfın yapılandırma dosyasındaki bir bilgiye ihtiyacı varsa, sınıfın onu nasıl alabileceğini çözmeye çalışmayın. Bunun yerine yalnızca isteyin, örneğin sınıfın yapıcısı üzerinden. Sonra bu değeri yapılandırma dosyasında verin.

Bu örnekte %myParameter%, MyClass yapıcısına aktarılacak myParameter parametresinin değerinin yer tutucusudur:

# config.neon
parameters:
	myParameter: Bir değer

services:
	- MyClass(%myParameter%)

Birden çok parametre aktarmak ya da autowiring kullanmak istiyorsanız, parametreleri bir nesnenin içine almak yararlıdır.

Nette, PSR-11 Container arayüzünü destekliyor mu?

Nette DI Container PSR-11'i doğrudan desteklemez. Ancak Nette DI Container ile PSR-11 Container arayüzünü bekleyen kütüphaneler ya da framework'ler arasında birlikte çalışabilirlik gerekiyorsa, Nette DI Container ile PSR-11 arasında köprü görevi görecek basit bir adaptör yazabilirsiniz.

Container, compiler, definition gibi terimler ne anlama geliyor?

Nette DI çevresinde, çoğu da extension yazarken sürekli karşınıza çıkan sözcüklerin kısa bir sözlüğü:

  • Container – servisleri istendiğinde oluşturan ve çalışma zamanında tutan derlenmiş nesne (Nette\DI\Container). Bir kez, iyileştirilmiş PHP kodu olarak üretilir.
  • Compiler – yapılandırma dosyalarını ve extension'ları bu container sınıfına dönüştüren düzenek.
  • ContainerBuilder – derleme sırasında kullanılan, container'ın değiştirilebilir modeli; gerçek bir servis var olmadan önce servis tanımlarını tutar. Bkz. Extension yazma.
  • Servis – container'ın yönettiği, genellikle bir kez oluşturulup paylaşılan (singleton) nesne: veritabanı bağlantısı, mailer, logger.
  • Definition – bir servisin tarifi: türü, nasıl oluşturulacağı ve sonrasında ne yapılacağı. Nette, tanımları container'ın factory metotlarına dönüştürür; birkaç türü vardır (bkz. tanım türleri).
  • Tür – servisin sınıfı ya da arayüzü; autowiring, servisleri onlara ihtiyaç duyan yerlerle eşleştirmek için bunu kullanır.
  • Autowiring – servisleri türlerine göre yapıcılara ve metotlara otomatik olarak aktarma; böylece bağımlılıkları elle bağlamazsınız.
  • Etiket – bir tanıma iliştirilen etiket (isteğe bağlı olarak bir değerle); bir extension daha sonra onu taşıyan tüm servisleri findByTag() ile bulabilir.
  • Setup – bir servis oluşturulduktan hemen sonra üzerinde yapılan ek işlemler: addSetup() ile eklenen metot çağrıları ya da özellik atamaları.
  • Alias – var olan bir servisin başka bir adı.
versiyon: 3.x