Bağımlılık enjeksiyonunun eleştirisi ve dezavantajları


118

Bağımlılık enjeksiyonu (DI) iyi bilinen ve şık bir kalıptır. Mühendislerin çoğu, avantajlarını biliyorlar:

  • Ünite testlerinde izolasyonu mümkün kılmak / kolaylaştırmak
  • Bir sınıfın bağımlılıklarını açıkça tanımlamak
  • İyi tasarımın kolaylaştırılması ( örneğin, tek sorumluluk prensibi (SRP))
  • Etkinleştirme hızlı bir şekilde (uygulamaları geçiş DbLoggeryerine ConsoleLoggerörneğin)

Sanırım DI'nin iyi ve faydalı bir model olduğu konusunda endüstri çapında bir fikir birliği var. Şu anda çok fazla eleştiri yok. Toplulukta belirtilen dezavantajlar genellikle küçüktür. Bazıları:

  • Artan sınıf sayısı
  • Gereksiz arayüzlerin oluşturulması

Şu anda mimarlık tasarımını meslektaşımla tartışıyoruz. O oldukça muhafazakar, ama açık fikirli. İyi olduğunu düşündüğüm şeyleri sorgulamayı seviyor, çünkü BT'deki birçok kişi sadece en yeni eğilimi kopyalıyor, avantajları tekrar ediyor ve genel olarak çok fazla düşünmüyor - çok fazla analiz etme.

Sormak istediğim şeyler:

  • Tek bir uygulamamız olduğunda bağımlılık enjeksiyonunu kullanmalı mıyız?
  • Dil / çerçeve dışındaki nesneler yaratmayı yasaklamalı mıyız?
  • Tek bir uygulamaya kötü bir fikir enjekte etmek mi (diyelim ki tek bir uygulamamız var, bu yüzden "boş" bir arayüz oluşturmak istemiyoruz), belirli bir sınıfı test etmek istemiyorsak?

33
Gerçekten bir desen olarak bağımlılık enjeksiyonunu mu soruyorsunuz, yoksa DI çerçevelerini mi kullanıyorsunuz? Bunlar gerçekten belirgin şeylerdir, sorunun hangi kısmıyla ilgilendiğinizi açıklığa kavuşturmanız veya her ikisini de açıkça sormanız gerekir.
Frax

10
@ Desen hakkında çerçeveler değil, çerçeveler
Landeeyo

10
Bağımlılık inversiyonunu bağımlılık enjeksiyonuyla karıştırıyorsunuz. İlki bir tasarım prensibidir. İkincisi, nesnelerin hiyerarşilerini oluşturmak için (genellikle mevcut bir araçla uygulanır) bir tekniktir.
jpmc26

3
Sık sık gerçek veritabanını kullanarak testler yazıyorum ve hiçbir alay konusu yok. Pek çok durumda gerçekten iyi çalışıyor. Ve sonra çoğu zaman arayüzlere ihtiyacınız olmaz. Eğer bir UserServicesınıfa sahipseniz , sadece mantık için bir tutucudur. Bir veritabanı bağlantısı enjekte edilir ve geriye alınan bir işlemin içinde yapılan testler. Birçoğu buna kötü bir uygulama diyebilir ama bunun son derece iyi çalıştığını gördüm. Sadece test için kodunuzu bozmanıza gerek yok ve entegrasyon testlerinde hata bulma gücünüz var.
usr

3
DI neredeyse her zaman iyidir. Kötü olan şey bir çok insanın DI tanıdıklarını düşünmeleridir ancak tüm bildikleri, ne yaptıklarından emin olmadan bile tuhaf bir çerçeveyi nasıl kullanacakları. DI bugünlerde Kargo Kültü Programcılığından çok acı çekiyor.
T. Sar

Yanıtlar:


160

İlk olarak, tasarım yaklaşımını çerçeveler kavramından ayırmak istiyorum. En basit ve en temel düzeyde bağımlılık enjeksiyonu basitçe:

Bir üst nesne, alt nesne için gereken tüm bağımlılıkları sağlar.

Bu kadar. Bunun içindeki hiçbir şeyin arayüzler, çerçeveler, herhangi bir enjeksiyon tarzı, vb. Gerektirmediğine dikkat edin. Adil olmak gerekirse, ilk önce bu deseni 20 yıl önce öğrendim. Yeni değil.

Bağımlılık enjeksiyonu bağlamında ebeveyn ve çocuk teriminde karışıklığa sahip 2'den fazla kişi olması nedeniyle:

  • Üst öğe , kullandığı alt nesneyi başlatan ve yapılandıran nesnedir.
  • Çocuk pasif örneklenemez şekilde tasarlanmıştır bileşendir. Diğer bir deyişle, ebeveyn tarafından sağlanan bağımlılıkları kullanmak üzere tasarlanmıştır ve kendi bağımlılıklarını somutlaştırmaz.

Bağımlılık enjeksiyonu, nesne kompozisyonu için bir kalıptır .

Neden arayüzler?

Arayüzler bir sözleşmedir. İki nesnenin ne kadar sıkı bir şekilde bağlanabileceğini sınırlamak için varlar. Her bağımlılık bir arayüze ihtiyaç duymaz, ancak modüler kod yazarken yardımcı olurlar.

Birim testi kavramına eklediğinizde, herhangi bir arabirim için iki kavramsal uygulamaya sahip olabilirsiniz: uygulamanızda kullanmak istediğiniz gerçek nesne ve nesneye bağlı olan kodu test etmek için kullandığınız alaylı veya saplanmış nesne. Bu tek başına arayüz için yeterli bir gerekçe olabilir.

Neden çerçeveler?

Temel olarak, alt nesnelere başlangıç ​​yapmak ve bağımlılık sağlamak, çok sayıda olduğunda korkutucu olabilir. Altyapıları aşağıdaki avantajları sağlar:

  • Bileşenlere otomatik kablolama bağımlılıkları
  • Bileşenleri bir tür ayarlarla yapılandırma
  • Kazan plakası kodunu otomatik hale getirin, böylece birden fazla yerde yazılı olarak görmenize gerek kalmaz.

Ayrıca aşağıdaki dezavantajlara da sahiptirler:

  • Üst nesne bir "kapsayıcı" dır ve kodunuzda hiçbir şey yoktur.
  • Bağımlılıkları doğrudan test kodunuzda sağlayamazsanız, testi daha karmaşık hale getirir.
  • Yansıma ve diğer pek çok hileyi kullanarak tüm bağımlılıkları çözdüğü için başlatmayı yavaşlatabilir
  • Çalışma zamanı hata ayıklaması, özellikle kap arabirim ile arabirimi uygulayan asıl bileşen arasına bir proxy enjekte ederse daha zor olabilir (Spring'e yerleşik en-boy yönelimli programlama akla gelir). Konteyner kara bir kutu ve her zaman hata ayıklama işlemini kolaylaştıran herhangi bir konseptle oluşturulmamışlar.

Tüm söylenenler, takaslar var. Çok fazla hareketli parça bulunmayan küçük projeler için ve DI çerçevesini kullanmak için çok az neden vardır. Ancak, zaten sizin için yapılmış belirli bileşenlerin bulunduğu daha karmaşık projeler için çerçeve haklı görülebilir.

Peki ya [internetteki rastgele makale]?

Ne hakkında? Çoğu zaman insanlar fazlaca kısılabilir ve bir dizi kısıtlama ekleyebilir ve "doğru yol" yapmazsanız sizi rahatsız edebilir. Tek bir doğru yol yok. Makaleden yararlı bir şey çıkarıp çıkarmayacağınıza bakın ve aynı fikirde olmadığınız şeyleri görmezden gelin.

Kısacası, kendiniz için düşünün ve bir şeyler deneyin.

"Eski kafalarla" çalışmak

Mümkün olduğu kadar çok şey öğrenin. 70'lerinde çalışan birçok geliştiriciyle bulacağınız şey, birçok şey hakkında dogmatik olmamaları gerektiğini öğrenmeleridir. Onlarca yıl boyunca birlikte çalıştığı, doğru sonuçlar veren yöntemlere sahipler.

Bunlardan birkaçıyla çalışma ayrıcalığına sahip oldum ve onlar çok mantıklı bir şekilde acımasızca dürüst geri bildirimde bulunabilirler. Ve değeri gördükleri yerde, bu araçları repertuarlarına eklerler.


6
@CarlLeth, Spring'den .net türevlerine kadar birçok çerçevede çalıştım. Bahar, bazı yansıma / sınıf yükleyici kara büyülerini kullanarak uygulamaları doğrudan özel alanlara enjekte etmeni sağlayacaktır. Bu şekilde yapılmış bileşenleri test etmenin tek yolu kabı kullanmaktır. Spring, test ortamını yapılandırmak için JUnit koşucularına sahiptir, ancak işleri ayarlamaktan daha karmaşıktır. Yani evet, pratik bir örnek verdim .
Berin Loritsch

17
Sorun giderici / bakıcı şapkamı takarken DI ile çerçeveler arasında bir engel olduğunu tespit ettiğim bir dezavantaj daha var: Verdikleri mesafedeki ürkütücü hareket çevrimdışı hata ayıklamayı zorlaştırıyor. En kötü durumda, bağımlılıkların nasıl başlatıldığını ve aktarıldığını görmek için kodu çalıştırmam gerekiyor. Bunu "test etme" bağlamında söylüyorsunuz, ancak kaynağa bakmaya yeni başlıyorsanız, asla Çalıştırmaya çalışırken akla (ki bu bir ton kurulum gerektirebilir). Sadece kodlara bakarak hangi kodun yapıldığını söyleme yeteneğime engel olmak Kötü Bir Şey.
Jeroen Mostert

1
Arayüzler sözleşmeler değildir, sadece API'lerdir. Sözleşmeler anlambilimi ima eder. Bu cevap, dile özgü terminoloji ve Java / C # özel kuralları kullanıyor.
Frank Hileman

2
@BerinLoritsch Kendi cevabınızın asıl amacı DI ilkesinin! = Herhangi bir DI çerçevesinin olmasıdır. Bahar'ın korkunç, affedilmez şeyler yapabilmesi gerçeği, genel olarak DI çerçevelerinin değil Baharın bir dezavantajıdır. İyi bir DI çerçevesi, DI prensibini kötü numaralar olmadan izlemenize yardımcı olur.
Carl Leth

1
@CarlLeth: tüm DI çerçeveleri, programcının hecelemek istemediği bazı şeyleri kaldırmak veya otomatikleştirmek için tasarlanmıştır, sadece nasıl değiştiğine göre değişir. Bildiğim kadarıyla, hepsi A ve B sınıfının sadece A ve B'ye bakarak nasıl (veya eğer ) etkileşime girdiğini bilme yeteneğini ortadan kaldırıyorlar - en azından, DI kurulumuna / konfigürasyonuna / sözleşmeler. Programcı için bir sorun değil (aslında tam olarak istedikleri şey), ancak bir bakımcı / hata ayıklayıcı için olası bir sorun (muhtemelen aynı programcı, daha sonra). Bu, DI çerçeveniz "mükemmel" olsa bile yapacağınız bir işlemdir.
Jeroen Mostert

88

Bağımlılık enjeksiyonu, çoğu kalıpta olduğu gibi, sorunlara bir çözümdür . Öyleyse, en başta sorunun olup olmadığını sorarak başlayın. Aksi takdirde, modelin kullanılması büyük olasılıkla kodu daha da kötüleştirecektir .

Bağımlılıkları azaltabilir veya ortadan kaldırabilirseniz önce düşünün. Diğer tüm şeyler eşit olmak üzere, bir sistemdeki her bir bileşenin mümkün olduğunca az bağımlılığına sahip olmasını istiyoruz. Bağımlılıklar bittiğinde, enjekte etme ya da sormama tartışma konusu olur!

Bazı verileri harici bir servisten indiren, çözümleyen ve karmaşık analizler yapan ve bir dosyaya ulaşmak için yazan bir modül düşünün.

Şimdi, harici hizmete bağımlılık kodlanmışsa, o zaman bu modülün dahili işlemlerini test etmek gerçekten zor olacaktır. Böylece, harici servisi ve dosya sistemini arabirim bağımlılığı olarak enjekte etmeye karar verebilirsiniz, bu da bunun yerine alaycı enjekte etmenize izin verir;

Ancak çok daha iyi bir çözüm, analizi girdi / çıktıdan ayırmaktır. Analiz, yan etkileri olmayan bir modüle çıkarılırsa, test edilmesi çok daha kolay olacaktır. Alaycının kod kokusu olduğunu unutmayın - her zaman kaçınılmaz değildir, ancak genel olarak alay etmeden güvenebilirsiniz. Dolayısıyla, bağımlılıkları ortadan kaldırarak, DI'nin hafifletmesi gereken sorunlardan kaçınırsınız. Böyle bir tasarımın SRP'ye daha iyi uyduğunu unutmayın.

DI'nin, SRP'yi veya endişelerin ayrılması, yüksek uyum / düşük eşleşme vb. Gibi diğer iyi tasarım ilkelerini zorunlu kılmadığını vurgulamak isterim. Tam tersi bir etkiye sahip olabilir. Dahili olarak başka bir B sınıfı kullanan A sınıfı düşünün. B sadece A tarafından kullanılır ve bu nedenle tamamen kapsüllenir ve bir uygulama detayı olarak düşünülebilir. Bunu, A'nın yapıcısına B enjekte etmek için değiştirirseniz, o zaman bu uygulama detayını ve şimdi bu bağımlılık ve B'nin nasıl başlatılacağı hakkındaki bilgiyi açığa çıkarmış olursunuz, B'nin ömrünün ayrı olması ve sistemde başka bir yerde bulunması gerekir. Yani A. Sızdıran endişeleri olan genel olarak daha kötü bir mimariye sahipsiniz.

Öte yandan, DI'nin gerçekten yararlı olduğu bazı durumlar vardır. Örneğin, logger gibi yan etkileri olan global hizmetler için.

Sorun, kalıpların ve mimarilerin araçlardan çok kendi başlarına amaç haline gelmesidir. Sadece "DI kullanmalı mıyız?" arabayı attan önce koymak bir nevidir. "Bir sorunumuz mu var?" Diye sormalısınız. ve "Bu sorun için en iyi çözüm nedir?"

Sorunuzun bir kısmı şu şekildedir: "Desenin taleplerini karşılamak için gereksiz arayüzler oluşturmalı mıyız?" Muhtemelen zaten bunun cevabını anlıyorsunuz - kesinlikle değil ! Size aksi söyleyen biri size bir şeyler satmaya çalışıyor - büyük olasılıkla pahalı danışmanlık saatleri. Bir arayüz sadece bir soyutlamayı temsil ediyorsa değere sahiptir. Tek bir sınıfın yüzeyini taklit eden bir arayüze "başlık arayüzü" denir ve bu bilinen bir antipattern'dir.


15
Daha fazla katılamadım! Ayrıca, bunun uğruna bir şeyleri alay etmenin, gerçek uygulamaları test etmediğimiz anlamına geldiğine dikkat edin. Eğer Akullanan Büretimde, fakat sadece karşı test edilmiştir MockBüretimde çalışacak eğer, testlerimiz bize söylemiyorlar. Bir etki alanı modelinin saf (yan etki yok) bileşenleri birbirlerini enjekte edip alay ediyorsa, sonuç herkesin zamanının büyük bir israfı, şişirilmiş ve kırılgan bir kod temeli ve sonuçta ortaya çıkan sisteme düşük güvenirliktir. Aynı sistemin keyfi parçaları arasında değil, sistemin sınırlarında alay.
Warbo

17
@CarlLeth Neden DI kodunu "test edilebilir ve bakımı yapılabilir" kılıyor ve kodunu daha az olmadan kullanıyorsunuz? JacquesB, yan etkilerin test edilebilirliği / sürdürülebilirliği zedeleyen şey olduğu konusunda haklıdır . Kodun yan etkisi yoksa, neyin / nerede / ne zaman / diğer kodu nasıl çağırdığı umrumda değil; basit ve doğrudan tutabiliriz. Kodun yan etkileri varsa , dikkat etmemiz gerekir . DI yan etkileri fonksiyonlardan çıkarabilir ve parametrelere koyabilir, bu fonksiyonları daha test edilebilir ancak program daha karmaşık hale getirir. Bazen bu kaçınılmazdır (örn. DB erişimi). Kodun hiçbir yan etkisi yoksa, DI sadece işe yaramaz bir karmaşıklıktır.
Warbo

13
@CarlLeth: DI, onu yasaklayan bağımlılıklara sahipse, kodun izolasyonda test edilebilir hale getirilmesi sorununa bir çözümdür. Ancak genel karmaşıklığı azaltmaz, kodu daha okunaklı hale getirmez, bu da sürdürülebilirliği zorunlu olarak artırmadığı anlamına gelir. Bununla birlikte, tüm bu bağımlılıklar endişelerin daha iyi ayrılmasıyla ortadan kaldırılabiliyorsa, bu DI'nin yararlarını tamamen geçersiz kılar, çünkü DI ihtiyacını ortadan kaldırır. Bu genellikle kodu aynı anda daha test edilebilir ve daha iyi hale getirmek için daha iyi bir çözümdür .
Doc Brown

5
@Warbo Bu orijinaldi ve hala muhtemelen sahte olan tek geçerli kullanımdı. Sistem sınırlarında bile, nadiren gereklidir. İnsanlar neredeyse değersiz testler oluşturmak ve güncellemek için çok zaman harcarlar.
Frank Hileman

6
@CarlLeth: tamam, şimdi yanlış anlamanın nereden geldiğini anlıyorum. Bağımlılık inversiyonu hakkında konuşuyorsunuz . Ancak, soru, bu cevap ve yorumlarım DI = bağımlılık enjeksiyonuyla ilgili .
Doc Brown

36

Tecrübelerime göre, bağımlılık enjeksiyonunun birkaç dezavantajı vardır.

Öncelikle, DI kullanımı, otomatikleştirilmiş testi reklamı kadar kolaylaştırmaz. Bir sınıfı sahte bir arabirim uygulamasıyla test eden birim, o sınıfın arabirimle nasıl etkileşimde bulunacağını doğrulamanıza olanak tanır. Diğer bir deyişle, test edilen sınıfın arayüz tarafından sağlanan sözleşmeyi nasıl kullandığını birim test etmenize izin verir. Bununla birlikte, bu test edilen sınıftan ara yüze girişin beklendiği gibi olduğu konusunda çok daha büyük bir güvence sağlar. Test altındaki sınıfın arayüzden çıkması beklendiği gibi yanıt vermesi oldukça zayıf bir güvence sağlar, çünkü neredeyse evrensel olarak sahte çıktı; Kısacası, sınıfın gerçek bir arayüz uygulamasıyla beklendiği gibi davranacağını doğrulamanıza DEĞİLDİR.

İkincisi, DI kodda gezinmeyi çok zorlaştırır. İşlevlere girdi olarak kullanılan sınıfların tanımına gitmeye çalışırken, bir arayüz küçük bir sıkıntıdan (örneğin tek bir uygulamanın olduğu yerlerde) büyük bir zaman havuzuna (örneğin, IDisposable gibi aşırı genel bir arayüz kullanıldığında) herhangi bir şey olabilir. Kullanılan gerçek uygulamayı bulmaya çalışırken. Bu, "Bu günlük ifadesinin yazdırılmasından hemen sonra gelen kodda boş bir referans istisnasını düzeltmem gerekiyor" gibi basit bir alıştırmayı bir gün sürecek bir çabaya dönüştürebilir.

Üçüncüsü, DI ve çerçevelerin kullanımı iki ucu keskin bir kılıçtır. Genel işlemler için gereken kazan plakası kod miktarını büyük ölçüde azaltabilir. Bununla birlikte, bu, bu ortak işlemlerin gerçekte nasıl bir araya getirildiğini anlamak için özel DI çerçevesi hakkında ayrıntılı bilgiye ihtiyaç duyma pahasına gelir. Bağımlılıkların çerçeveye nasıl yüklendiğini anlamak ve enjekte edilecek çerçeveye yeni bir bağımlılık eklemek, makul miktarda arka plan materyali okumayı ve çerçevedeki bazı temel dersleri takip etmeyi gerektirebilir. Bu, bazı basit görevleri zaman alıcı hale getirebilir.


Ayrıca ne kadar çok enjekte edersen, başlangıç ​​zamanın da o kadar uzar. DI çerçevelerinin çoğu, nerede kullanıldıklarına bakılmaksızın, başlangıç ​​sırasında enjekte edilebilir tüm singleton örneklerini oluşturur.
Rodney P. Barbati

7
Bir sınıfı gerçek uygulama ile test etmek istiyorsanız (sahte değil), fonksiyonel testler yazabilirsiniz - birim testlerine benzer testler, ancak sahte kullanmayın.
BЈовић

2
İkinci paragrafınızın daha nüanslı olması gerektiğini düşünüyorum: Kendi içinde DI kodda gezinmeyi zorlaştırmaz. En basit haliyle, DI sadece SOLID izlemenin bir sonucudur. Karmaşıklığı artıran, gereksiz dolaylı ve DI çerçevelerin kullanılmasıdır . Bunun dışında, bu cevap kafanın çivisine çarpar.
Konrad Rudolph

4
Bağımlılık enjeksiyonu, gerçekten ihtiyaç duyulan durumlar dışında, diğer gereksiz kodların bolca bulunabileceğinin de bir işaretidir. Yöneticiler genellikle geliştiricilerin karmaşıklık uğruna karmaşıklık eklediğini öğrenince şaşırır.
Frank Hileman

1
+1. Bu, sorulan soruya gerçek cevaptır ve kabul edilen cevap olması gerekir.
Mason Wheeler

13

Tahmin etmek için Mark Seemann'in ".NET'te Bağımlılık Enjeksiyonu" başlıklı tavsiyesine uydum.

“Uçucu bir bağımlılık” olduğunda DI kullanılmalıdır, örneğin değişmesi makul bir ihtimaldir.

Dolayısıyla, gelecekte birden fazla uygulamaya sahip olabileceğinizi veya uygulamanın değişebileceğini düşünüyorsanız, DI kullanın. Aksi takdirde newiyidir.


5
Ayrıca OO ve fonksiyonel diller için de farklı tavsiyeler veriyor
.

1
Bu iyi bir nokta. Varsayılan olarak her bağımlılık için arayüzler oluşturursak, bu bir şekilde YAGNI'ye karşıdır.
Landeeyo

4
".NET'e bağımlılık enjeksiyonu" için bir referans verebilir misiniz?
Peter Mortensen

1
Birim testi yapıyorsanız, bağımlılığınızın değişken olması muhtemeldir.
Jaquez

11
İşin iyi yanı, geliştiriciler her zaman geleceği mükemmel doğrulukla tahmin edebilmektir.
Frank Hileman

5

DI ile ilgili en büyük evcil hayvan peeling'imden birkaç cevapta çoktan bahsedilmişti, ancak burada biraz daha genişleyeceğim. DI (bugün çoğunlukla kaplarda vb. Yapıldığı gibi) gerçekten, gerçekten kod okunabilirliğine zarar veriyor. Kod okunabilirliği tartışılabilir bir şekilde günümüzün programlama yeniliklerinin çoğunun arkasındaki nedenidir. Birinin dediği gibi - kod yazmak kolaydır. Kod okumak zor. Ama aynı zamanda, çok küçük, bir kez yazma özelliğine sahip bir yardımcı program yazmadığınız sürece de çok önemlidir.

Bu konuda DI ile ilgili sorun, bunun opak olmasıdır. Konteyner kara bir kutudur. Nesneler basitçe bir yerden ortaya çıkıyor ve hiçbir fikriniz yok - onları kim ve ne zaman yaptı? Yapıcıya ne geçti? Bu örneği kimlerle paylaşıyorum? Kim bilir...

Öncelikle arayüzlerle çalışırken, IDE'nizin tüm "tanımlara git" özellikleri duman içinde yükseliyor. Programın akışını, çalıştırmadan izlemek ve sadece WHICH'in bu uygulamadaki WHICH uygulamasının bu noktada kullanıldığını görmek için adım atmak çok zor. Ve zaman zaman adım atmanızı engelleyen teknik bir engel var. Ve yapabilseniz bile, DI kabının kıvrılmış bağırsaklarından geçerse bile, bütün mesele çabucak hayal kırıklığı içinde bir egzersiz haline gelir.

DI'yi kullanan bir kod parçasıyla verimli bir şekilde çalışmak için, ona aşina olmanız ve ne nereye gittiğini zaten bilmeniz gerekir .


3

Anahtarlama uygulamalarını hızlı bir şekilde etkinleştirme (örneğin, ConsoleLogger yerine DbLogger)

Genel olarak DI kesinlikle iyi bir şey olsa da, bunu her şey için kör kullanmamanızı öneririm. Mesela ben hiç logger enjekte etmedim. DI'nin avantajlarından biri bağımlılıkları açık ve net hale getirmektir. ILoggerNeredeyse her sınıfın bağımlılığı olarak listelemenin anlamı yok - bu sadece karışıklık. İhtiyacınız olan esnekliği sağlamak logger'ın sorumluluğundadır. Tüm kayıt cihazlarım statik son üyelerdir, statik olmayan bir gruba ihtiyacım olduğunda bir kayıt cihazını enjekte etmeyi düşünebilirim.

Artan sınıf sayısı

Bu, DI'nin değil, verilen DI çerçevesinin veya alaycı çerçevenin bir dezavantajıdır. Çoğu yerde sınıflarım somut sınıflara dayanıyor, bu da gerekli sıfır plakanın olduğu anlamına geliyor. Guice (bir Java DI çerçevesi) varsayılan olarak bir sınıfı kendisine bağlar ve yalnızca testlerde bağlamayı geçersiz kılmam gerekiyor (ya da bunları manuel olarak bağlamanız gerekiyor).

Gereksiz arayüzlerin oluşturulması

Sadece gerektiğinde arayüzleri oluşturuyorum (bu oldukça nadirdir). Bu, bazen, bir sınıfın tüm oluşumlarını bir arabirim ile değiştirmem gerektiği anlamına gelir, ancak IDE bunu benim için yapabilir.

Tek bir uygulamamız olduğunda bağımlılık enjeksiyonunu kullanmalı mıyız?

Evet, ancak herhangi bir kazan plakası eklemekten kaçının .

Dil / çerçeve dışındaki nesneler yaratmayı yasaklamalı mıyız?

Hayır. Çok sayıda değer (değişmez) ve veri (değişken) sınıfları olacak, örneklerin henüz yaratıldığı ve geçtiği ve onları enjekte etmenin hiçbir anlamı olmadığı - başka bir nesnede (ya da yalnızca diğerlerinde asla saklanmadıkları) bu tür nesneler).

Onlar için bunun yerine bir fabrika enjekte etmeniz gerekebilir, ancak çoğu zaman hiçbir anlamı yoktur (örneğin, @Value class NamedUrl {private final String name; private final URL url;}burada gerçekten bir fabrikaya ihtiyacınız yok ve enjekte edilecek bir şey yok).

Tek bir uygulamaya kötü bir fikir enjekte etmek mi (diyelim ki tek bir uygulamamız var, bu yüzden "boş" bir arayüz oluşturmak istemiyoruz), belirli bir sınıfı test etmek istemiyorsak?

IMHO, kod şişmesine neden olmadığı sürece sorun değil. Bağımlılığı enjekte edin, ancak daha sonra herhangi bir güçlük çekmeden yapabileceğiniz gibi arayüzü (ve çılgın yapılandırma XML!) Oluşturmayın.

Aslında, şu anki projemde, veri nesneleri dahil çok fazla yerde kullanılan basit sınıflar olduğu için DI dışında bırakmaya karar verdiğim dört sınıf (yüzlerce) var .


Çoğu DI çerçevesinin bir başka dezavantajı, çalışma zamanı ek yüküdür. Bu derleme zamanına taşınabilir (Java için hançer var , diğer diller hakkında hiçbir fikrim yok).

Yine bir başka dezavantaj, her yerde gerçekleşen sihirdir, ki bunlar ayarlanabilir (örn. Guice kullanırken proxy oluşturma işlemini engelliyorum).


-4

Kanımca, Bağımlılık Enjeksiyonunun bütün kavramının geçersiz kıldığını söylemeliyim.

DI, küresel değerlerin günümüzdeki eşdeğeridir. Enjekte ettiğiniz şeyler küresel tekiller ve saf kod nesneleridir, aksi halde bunları enjekte edemezsiniz. Belirli bir kütüphaneyi (JPA, Spring Data, vb.) Kullanmak için DI kullanımının çoğu size zorlanır. DI, çoğunlukla, spagetti beslemek ve yetiştirmek için mükemmel ortamı sağlar.

Dürüst olmak gerekirse, bir sınıfı sınamanın en kolay yolu, tüm bağımlılıkların geçersiz kılınabilecek bir yöntemle oluşturulmasını sağlamaktır. Sonra gerçek sınıftan türetilmiş bir Test sınıfı oluşturun ve bu yöntemi geçersiz kılın.

Ardından Test sınıfını başlatır ve tüm yöntemlerini test edersiniz. Bu, bazılarınız için net olmayacak - test ettiğiniz yöntemler, test edilen sınıfa ait olanlardır. Ve bu yöntem testlerinin tümü tek bir sınıf dosyasında (test edilen sınıfa bağlı birim test sınıfı) gerçekleşir. Burada genel gider sıfırdır - ünite testi bu şekilde çalışır.

Kodda, bu kavram şuna benziyor ...

class ClassUnderTest {

   protected final ADependency;
   protected final AnotherDependency;

   // Call from a factory or use an initializer 
   public void initializeDependencies() {
      aDependency = new ADependency();
      anotherDependency = new AnotherDependency();
   }
}

class TestClassUnderTest extends ClassUnderTest {

    @Override
    public void initializeDependencies() {
      aDependency = new MockitoObject();
      anotherDependency = new MockitoObject();
    }

    // Unit tests go here...
    // Unit tests call base class methods
}

Sonuç DI'yi kullanmaya tamamen eşdeğerdir, yani ClassUnderTest test için yapılandırılmıştır.

Tek fark bu kodun tamamen özlü, tamamen kapalı, kodlaması kolay, anlaşılması kolay, daha hızlı, daha az bellek kullanması, alternatif bir yapılandırma gerektirmemesi, herhangi bir çerçeve gerektirmemesi, asla 4 sayfanın nedeni olmamasıdır. (WTF!) Yazdığınız ve tam olarak başlangıç ​​seviyesinden Guru'ya kadar en düşük OO bilgisine sahip olan herkes için tamamen açık olan SERO (0) sınıflarını içeren yığın izi.

Olduğu söyleniyor, elbette kullanamayız - bu çok açık ve yeterince moda değil.

Günün sonunda, DI ile ilgili en büyük endişem, sefil bir başarısızlık gördüğüm projelerden biriydi, hepsi DI'nin her şeyi bir arada tutan tutkalı olduğu büyük kod üsleriydi. DI bir mimari değildir - yalnızca birçoğu başka bir kütüphane (JPA, Spring Data, vb.) Kullanmak için zorlanan bir avuç durumla ilgilidir. Çoğu zaman, iyi tasarlanmış bir kod tabanında, DI kullanımının çoğu, günlük gelişim faaliyetlerinizin gerçekleştiği seviyenin altında gerçekleşir.


6
Eşdeğeri değil, bağımlılık enjeksiyonunun tersini tanımladınız. Modelinizde, her nesnenin tüm bağımlılıklarının somut uygulamasını bilmesi gerekir, ancak uygun uygulamaları bir arada yapıştırmak için "ana" bileşenin sorumluluğunda olan DI'yi kullanmak gerekir. DI'nin diğer DI bağımlılık inversiyonu ile el ele gittiğini , yüksek seviye bileşenlerin düşük seviye bileşenlerde zor bağımlılıklara sahip olmasını istemediğinizi unutmayın.
Logan Pickup

1
yalnızca bir sınıf kalıtım seviyesi ve bir bağımlılık seviyeniz olduğunda deriz. Elbette, genişledikçe yeryüzünde cehenneme dönüşecek?
Ewan

4
Eğer arayüzleri kullanıyorsanız ve initializeDependencies () 'yi yapıcıya taşıyın, aynı ama daha temiz. Bir sonraki adım, inşaat parametreleri eklemek, tüm TestClass'larınızdan kurtulabileceğiniz anlamına gelir.
Ewan

5
Bunda çok yanlış var. Diğerlerinin de söylediği gibi, 'DI eşdeğeri' örneğiniz bağımlılık enjeksiyonu değildir, antitezdir ve kavramı tam olarak anlamadığını gösterir ve diğer potansiyel tuzaklar da ortaya çıkarır: kısmen başlatılmış nesneler bir kod kokusu gibi öneriyor, başlatıcıyı yapıcıya taşı ve bunları yapıcı parametreleriyle ilet. O zaman DI var ...
Mr.Mindor

3
@ Mr.Mindor'a ekleme: Sadece başlatma için geçerli olmayan daha genel bir anti-patern "sıralı eşleşme" var. Bir nesnenin yöntemleri (veya daha genel olarak bir API çağrıları) belirli bir sırada çalıştırılmalıdır, örneğin baryalnızca sonra çağrılabilir foo, o zaman bu kötü bir API'dir. İşlevsellik ( ) sağladığı iddiası var bar, ancak bunu gerçekten kullanamayız (çünkü fooçağrılmamış olabilir). Eğer sopa ile isterseniz initializeDependencies(anti?) Desen, en azından korumalı / Özel yapmak ve yapıcı otomatik diyoruz, API, samimidir olmalıdır.
Warbo

-6

Gerçekten sorunuz "birim testi kötü mü?"

Enjekte edilecek alternatif sınıflarınızın% 99'u ünite testini mümkün kılan alaylar olacaktır.

DI olmadan birim testi yaparsanız, sınıfların sahte verileri veya sahte servisi kullanmalarını sağlama probleminiz vardır. Hizmetlere ayırmayacağınız için 'mantığın bir parçası' diyelim.

Bunu yapmanın alternatif yolları var, ancak DI iyi ve esnek. Test için bir kez girdikten sonra, hemen hemen her yerde kullanmaya zorlanırsınız, çünkü başka bir kod parçasına gereksinim duyuyorsunuz, hatta bu, beton türlerini örnekleyen 'zavallı adamın DI'si' bile.

Bir dezavantajı hayal etmek zor, ünite testinin avantajlarından çok etkilenmiş.


13
DI'nin birim testi ile ilgili olduğu iddiasına katılmıyorum. Ünite testini kolaylaştırmak, DI'nin faydalarından sadece biridir ve tartışmasız en önemlisi değildir.
Robert Harvey

5
Sizin biriminize katılmıyorum ünite testi ve DI çok yakın. Bir taklit / saplama kullanarak, test odasının bize biraz daha fazla yalan söylemesini sağlıyoruz: test edilen sistem gerçek sistemden daha da ileri gidiyor. Bu nesnel olarak kötü. Bazen bu tersine ağır basar: alaylı FS çağrıları temizleme gerektirmez; alay edilmiş HTTP istekleri hızlı, deterministik ve çevrimdışı çalışır; vs. Buna karşın, newbir yöntemin içine kodlanmış her kod kullandığımızda, üretimde aynı kodun testler sırasında çalıştığını biliyoruz .
Warbo

8
Hayır, “ünite testi kötü mü?”, “Alaycı (a) gerçekten gerekli ve (b) karmaşıklığın artmasına değer mi?” Bu çok farklı bir soru. Birim testi fena değil (kelimenin tam anlamıyla kimse bunu tartışmıyor) ve kesinlikle buna değer. Ancak tüm ünite testleri alay etmek zorunda değildir ve alay etmek önemli bir maliyet taşır, bu yüzden en azından makul bir şekilde kullanılması gerekir.
Konrad Rudolph

6
@Ewan Son yorumunuzdan sonra hemfikir olduğumuzu sanmıyorum. Birim testlerin çoğunun DI [çerçevelere] ihtiyacı olmadığını söylüyorum , çünkü çoğu birim testinde alay etmeye gerek yok. Aslında, bunu kod kalitesi için bir buluşsal yöntem olarak bile kullanırdım: kodunuzun çoğu DI / alay nesnesi olmadan birim test edilemezse, o zaman doğru bir şekilde bağlanmış olan kötü kod yazdınız. Kodların çoğu yüksek oranda ayrıştırılmış, tek sorumluluk ve genel amaçlı olmalı ve izolasyonda önemsiz şekilde test edilebilir olmalıdır.
Konrad Rudolph

5
@Ewan Bağlantınız, iyi bir birim sınama tanımı sağlar. Bu tanım gereğince Orderörneğim bir birim testidir: bir yöntemi test eder ( totalyöntemi Order). 2 sınıftan kod çağırdığından şikayet ediyorsun, cevabım ne ? "Bir kerede 2 sınıfı" test etmiyoruz, totalyöntemi test ediyoruz . Biz bakım gerekir bir yöntem işini yapar nasıl: O uygulama detaylar; Bunları test etmek kırılganlığa, sıkı bağlantıya vs. neden olur . Sınıfların / modüllerin / CPU kayıtlarının / vb. değil, sadece yöntemin davranışına (dönüş değeri ve yan etkiler) önem veriyoruz . sürecinde kullanılır.
Warbo
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.