İlk olarak, bu cevap için yaptığım varsayımı açıklamak istiyorum. Her zaman doğru değil, ancak sıklıkla:
Arayüzler sıfatlardır; sınıflar isimlerdir.
(Aslında, isimler de olan arayüzler var, ama burada genelleştirmek istiyorum.)
Yani, örneğin bir arayüz IDisposable
, IEnumerable
veya gibi bir şey olabilir IPrintable
. Sınıf, bu arabirimlerin bir veya daha fazlasının gerçek bir uygulamasıdır: List
veya Map
her ikisi de IEnumerable
.
Konuya ulaşmak için: Genellikle sınıflarınız birbirine bağlıdır. Örneğin Database
, veritabanınıza erişen bir sınıfa (hah, sürpriz! ;-)) sahip olabilirsiniz, ancak bu sınıfın veritabanına erişme hakkında günlük kaydı yapmasını da istersiniz. Başka bir sınıfınız olduğunu varsayalım Logger
, o zaman Database
bağımlılığınız var Logger
.
Çok uzak çok iyi.
Bu bağımlılığı Database
sınıfınızda aşağıdaki satırla modelleyebilirsiniz :
var logger = new Logger();
ve her şey yolunda. Bir sürü kaydediciye ihtiyacınız olduğunu fark ettiğiniz güne kadar iyi olur: Bazen konsola, bazen dosya sistemine, bazen TCP / IP ve uzak bir günlük sunucusu kullanarak oturum açmak istersiniz ...
Ve do kursu DEĞİL tüm kodunu (bu arada bunun gazillions var) değiştirip tüm hatları değiştirmek istiyor
var logger = new Logger();
tarafından:
var logger = new TcpLogger();
İlk olarak, bu eğlenceli değil. İkincisi, bu hataya yatkındır. Üçüncüsü, bu eğitimli bir maymun için aptal, tekrarlayan bir çalışma. Ee ne yapıyorsun?
Açıkçası ICanLog
, çeşitli kaydediciler tarafından uygulanan bir arayüz (veya benzeri) tanıtmak oldukça iyi bir fikirdir . Kodunuzdaki 1. adım, şunları yapmanızdır:
ICanLog logger = new Logger();
Artık tür çıkarımı artık türü değiştirmiyor, her zaman karşı karşıya gelecek tek bir arabirim var. Bir sonraki adım, new Logger()
tekrar tekrar sahip olmak istemediğinizdir . Böylece, tek bir merkezi fabrika sınıfına yeni örnekler oluşturma güvenilirliğini koyarsınız ve aşağıdaki gibi bir kod alırsınız:
ICanLog logger = LoggerFactory.Create();
Fabrikanın kendisi ne tür bir kaydedici oluşturacağına karar verir. Kodunuz artık umursamıyor ve kullanılan günlükçünün türünü değiştirmek istiyorsanız, kodu bir kez değiştirirsiniz : Fabrika içinde.
Şimdi, elbette, bu fabrikayı genelleştirebilir ve herhangi bir tür için çalışmasını sağlayabilirsiniz:
ICanLog logger = TypeFactory.Create<ICanLog>();
Bu TypeFactory'nin bir yerinde, belirli bir arabirim türü istendiğinde somutlaştırılacak gerçek sınıfın yapılandırma verilerine ihtiyacı vardır, bu nedenle bir eşlemeye ihtiyacınız vardır. Tabii ki bu eşlemeyi kodunuzun içinde yapabilirsiniz, ancak daha sonra bir tür değişikliği yeniden derleme anlamına gelir. Ancak bu eşlemeyi bir XML dosyasının içine de yerleştirebilirsiniz, örn. Bu, derleme süresinden (!) Sonra bile gerçekte kullanılan sınıfı değiştirmenizi sağlar, yani dinamik olarak yeniden derlemeden demektir!
Bunun için yararlı bir örnek vermek gerekirse: Normal olarak oturum açmayan bir yazılım düşünün, ancak müşteriniz bir sorun olduğu için yardım istediğinde, ona gönderdiğiniz tek şey güncellenmiş bir XML yapılandırma dosyasıdır ve şimdi günlüğe kaydetme etkinleştirilir ve desteğiniz, müşterinize yardımcı olmak için günlük dosyalarını kullanabilir.
Ve şimdi, isimleri biraz değiştirdiğinizde, Inversion of Control için iki modelden biri olan basit bir Servis Bulucu uygulamasıyla sonuçlanırsınız (çünkü kimin hangi sınıfın somutlaştırılacağına karar vermesini tersine çevirdiğinizden).
Tüm bunlar, kodunuzdaki bağımlılıkları azaltır, ancak şimdi tüm kodlarınız merkezi, tek servis bulucuya bağımlıdır.
Bağımlılık enjeksiyonu şimdi bu satırdaki bir sonraki adımdır: Sadece servis bulucuya olan bu tek bağımlılıktan kurtulun: Servis bulucudan belirli bir arayüz için bir uygulama istemesini isteyen çeşitli sınıflar yerine, bir kez daha kimin neyi örneklediğini kontrol edersiniz .
Bağımlılık enjeksiyonu ile, Database
sınıfınızda artık bir tür parametre gerektiren bir kurucu var ICanLog
:
public Database(ICanLog logger) { ... }
Artık veritabanınızın her zaman kullanmak için bir kaydedicisi vardır, ancak artık bu kaydedicinin nereden geldiğini bilmemektedir.
Ve burada bir DI çerçevesi devreye girer: Eşlemelerinizi bir kez daha yapılandırırsınız ve ardından DI çerçevenizden uygulamanızı sizin için somutlaştırmasını istersiniz. Gibi Application
sınıf bir gerektiren ICanPersistData
uygulama, bir örneği Database
enjekte edilir - ama bunun için öncelikle için yapılandırılmış kaydedici tür bir örneğini oluşturmalısınız ICanLog
. Ve bunun gibi ...
Yani, uzun bir hikaye kısaltmak için: Bağımlılık enjeksiyonu, kodunuzdaki bağımlılıkları nasıl kaldırmanın iki yolundan biridir. Derleme süresinden sonra yapılandırma değişiklikleri için çok yararlıdır ve birim testi için harika bir şeydir (saplamaları ve / veya alayları enjekte etmeyi çok kolaylaştırdığı için).
Uygulamada, bir servis bulucu olmadan yapamayacağınız şeyler vardır (örneğin, belirli bir arayüzde kaç örneğe ihtiyacınız olduğunu önceden bilmiyorsanız: Bir DI çerçevesi her parametre için yalnızca bir örnek enjekte eder, ancak tabii ki bir döngü içindeki bir servis bulucu), dolayısıyla her DI çerçevesi de bir servis bulucu sağlar.
Ama temelde, hepsi bu.
Not: Burada tarif ettiğim, yapıcı enjeksiyonu adı verilen bir tekniktir , ayrıca yapıcı parametreleri olmayan özellik enjeksiyonu da vardır , ancak özellikler bağımlılıkları tanımlamak ve çözmek için kullanılmaktadır. Özellik enjeksiyonunu isteğe bağlı bir bağımlılık ve yapıcı enjeksiyonunu zorunlu bağımlılıklar olarak düşünün. Ancak bu konudaki tartışma bu sorunun kapsamı dışındadır.