Bağımlılık Enjeksiyonu Kapsülleme pahasına mı gelmelidir?


128

Doğru anlarsam, Bağımlılık Enjeksiyonu için tipik mekanizma, ya bir sınıf yapıcısı ya da sınıfın bir genel özelliği (üyesi) aracılığıyla enjekte etmektir.

Bu, enjekte edilen bağımlılığı ortaya çıkarır ve OOP kapsülleme ilkesini ihlal eder.

Bu değiş tokuşu belirlemekte haklı mıyım? Bu konuyla nasıl başa çıkıyorsunuz?

Lütfen aşağıdaki kendi soruma verdiğim cevaba da bakınız.


7
bu çok akıllıca bir soru imho
dfa

5
İlk önce bu soruyu cevaplamak, kapsüllemenin ne anlama geldiğine dair bir tartışma gerektirir . ;)
Jeff Sternal

2
Kapsülleme, arayüzler tarafından sağlanır. Nesnenin temel özelliklerini ortaya çıkarırlar ve bağımlılıklar gibi ayrıntıları gizlerler. Bu, daha esnek bir konfigürasyon sağlamak için sınıfları bir dereceye kadar 'açmamıza' izin verir.
Lawrence Wagerfield

Yanıtlar:


62

Bu konuya bakmanın ilginç bulabileceğiniz başka bir yolu var.

IoC / bağımlılık enjeksiyonu kullandığımızda, OOP kavramlarını kullanmıyoruz. Kuşkusuz 'ana bilgisayar' olarak bir OO dili kullanıyoruz, ancak IoC'nin arkasındaki fikirler OO'dan değil, bileşen odaklı yazılım mühendisliğinden geliyor.

Bileşen yazılımı tamamen bağımlılıkları yönetmekle ilgilidir - yaygın kullanım için bir örnek .NET'in Assembly mekanizmasıdır. Her derleme, referans verdiği derlemelerin listesini yayınlar ve bu, çalışan bir uygulama için gereken parçaları bir araya getirmeyi (ve doğrulamayı) çok daha kolay hale getirir.

OO programlarımızda IoC aracılığıyla benzer teknikleri uygulayarak, programların yapılandırılmasını ve bakımını kolaylaştırmayı amaçlıyoruz. Yayın bağımlılıkları (yapıcı parametreleri veya her neyse) bunun önemli bir parçasıdır. Kapsülleme, bileşen / hizmet odaklı dünyada olduğu gibi gerçekten geçerli değildir, ayrıntıların sızacağı bir 'uygulama türü' yoktur.

Maalesef dillerimiz şu anda ince taneli, nesne yönelimli kavramları daha kaba bileşen yönelimli kavramlardan ayırmamaktadır, bu nedenle bu yalnızca aklınızda tutmanız gereken bir ayrımdır :)


18
Kapsülleme sadece süslü bir terminoloji parçası değildir. Gerçek faydaları olan gerçek bir şeydir ve programınızın "bileşen odaklı" veya "nesne yönelimli" olduğunu düşünmeniz önemli değildir. Kapsüllemenin, nesnenizin / bileşeninizin / hizmetinizin / her ne olursa olsun, beklenmedik şekillerde değiştirilmesini önlediği varsayılır ve IoC bu korumanın bir kısmını ortadan kaldırır, dolayısıyla kesinlikle bir ödünleşim vardır.
Ron Inbar

1
Bir kurucu aracılığıyla sağlanan argümanlar , nesnenin "değiştirilebileceği" beklenen yolların alanına girmeye devam eder : açıkça ortaya çıkarlar ve etraflarındaki değişmezler zorlanır. Bilgi gizleme , bahsettiğiniz gizlilik türü için daha iyi bir terimdir, @RonInbar ve her zaman yararlı olmayabilir (makarnanın çözülmesini zorlaştırır ;-)).
Nicholas Blumhardt

2
OOP'nin bütün noktası, makarna karmaşasının ayrı sınıflara ayrılması ve sadece değiştirmek istediğiniz belirli bir sınıfın davranışıysa, onunla uğraşmanız gerektiğidir (bu, OOP'nin karmaşıklığı nasıl azalttığıdır). Bir sınıf (veya modül), uygun bir genel arabirim ortaya çıkarırken kendi iç kısımlarını kapsüller (bu, OOP'nin yeniden kullanımı nasıl kolaylaştırdığıdır). Bağımlılıklarını arabirimler aracılığıyla ortaya çıkaran bir sınıf, istemcileri için karmaşıklık yaratır ve sonuç olarak daha az yeniden kullanılabilir. Aynı zamanda doğası gereği daha kırılgandır.
Neutrino

1
Hangi yönden bakarsam bakayım, bana öyle geliyor ki DI, OOP'nin en değerli faydalarından bazılarını ciddi şekilde baltalıyor ve henüz onu gerçekten çözecek şekilde kullanıldığını bulduğum bir durumla karşılaşmadım. mevcut sorun.
Neutrino

1
Sorunu çerçevelemenin başka bir yolu şudur: .NET derlemelerinin "kapsüllemeyi" seçip seçmediğini ve hangi diğer derlemelere güvendiklerini bildirmediğini düşünün. Belgeleri okumak ve yükledikten sonra bir şeyin işe yarayacağını ummak çılgınca bir durum olurdu. Bağımlılıkları bu düzeyde bildirmek, otomatik araçların uygulamanın büyük ölçekli bileşimi ile ilgilenmesine olanak tanır. Benzetmeyi görmek için gözlerinizi kısmanız gerekir, ancak bileşen düzeyinde benzer kuvvetler geçerlidir. Değiş-tokuşlar var ve her zamanki gibi YMMV :-)
Nicholas Blumhardt

29

Bu iyi bir soru - ama bir noktada, eğer nesne bağımlılığını yerine getirecekse, en saf haliyle kapsüllemenin ihlal edilmesi gerekiyor . Bazı bağımlılık sağlayıcısı, hem söz konusu nesnenin a gerektirdiğini hem de sağlayıcının nesneyi sağlamanın bir yolunu bulması gerektiğini bilmelidir .FooFoo

Klasik olarak bu ikinci durum, sizin söylediğiniz gibi yapıcı argümanları veya ayarlayıcı yöntemleri aracılığıyla ele alınır. Ancak, bu mutlaka doğru değildir - Java Bahar DI çerçevesinin son sürümleri, örneğin, (örneğin özel alanları açıklama izin biliyoruz @Autowired) ve bağımlılık size aracılığıyla bağımlılığı açığa gerek kalmadan yansıma yoluyla kurulacak sınıflardan herhangi biri genel yöntemler / yapıcılar. Bu, aradığınız türden bir çözüm olabilir.

Bununla birlikte, yapıcı enjeksiyonunun da bir sorun olduğunu düşünmüyorum. Her zaman nesnelerin inşa edildikten sonra tamamen geçerli olması gerektiğini düşünmüşümdür, öyle ki rollerini yerine getirmek için ihtiyaç duydukları her şey (yani geçerli bir durumda olmak) yine de kurucu tarafından sağlanmalıdır. Bir işbirlikçinin çalışmasını gerektiren bir nesneniz varsa, kurucunun bu gereksinimi kamuya duyurması ve sınıfın yeni bir örneği oluşturulduğunda bunun yerine getirilmesini sağlaması bana iyi geliyor.

İdeal olarak, nesnelerle uğraşırken, yine de bir arabirim aracılığıyla onlarla etkileşime girersiniz ve bunu ne kadar çok yaparsanız (ve DI aracılığıyla bağlılıklara sahip olursanız), aslında kurucularla o kadar az ilgilenmeniz gerekir. İdeal durumda, kodunuz sınıfların somut örnekleriyle uğraşmaz ve hatta bunları oluşturmaz; bu yüzden IFoo, kurucunun FooImplişini yapması için neye ihtiyacı olduğu konusunda endişelenmeden ve aslında varlığının farkında bile olmadan , bir aracılığıyla bir DI verilir FooImpl. Bu açıdan bakıldığında, kapsülleme mükemmel.

Bu elbette bir fikir, ancak bana göre DI kapsüllemeyi mutlaka ihlal etmiyor ve aslında gerekli tüm dahili bilgileri tek bir yerde merkezileştirerek ona yardımcı olabilir. Bu sadece kendi başına iyi bir şey değil, daha da iyisi burası kendi kod tabanınızın dışında, bu yüzden yazdığınız kodların hiçbirinin sınıfların bağımlılıkları hakkında bilgi sahibi olması gerekmiyor.


2
Güzel nokta. @Autowired'ı özel alanlarda kullanmamanızı öneririm; bu, sınıfı test etmeyi zorlaştırır; nasıl taklit veya taslak enjekte edersiniz?
lumpynose

4
Katılmıyorum. DI kapsüllemeyi ihlal ediyor ve bu önlenebilir. Örneğin, istemci sınıfı hakkında herhangi bir şey bilmesi gerekmeyen bir ServiceLocator kullanarak; yalnızca Foo bağımlılığının uygulamaları hakkında bilgi sahibi olması gerekir. Ancak çoğu durumda en iyi şey, sadece "yeni" operatörü kullanmaktır.
Rogério

5
@Rogerio - Muhtemelen herhangi bir DI çerçevesi tam olarak tanımladığınız ServiceLocator gibi davranır; müşteri Foo uygulamaları hakkında özel bir şey bilmiyor ve DI aracı müşteri hakkında özel bir şey bilmiyor. Ve sadece tam uygulama sınıfını değil, aynı zamanda ihtiyaç duyduğu tüm bağımlılıkların tam sınıflarını ve örneklerini de bilmeniz gerektiğinden, "yeni" yi kullanmak kapsüllemeyi ihlal etmek için çok daha kötüdür .
Andrzej Doyle

4
Genellikle herkese açık bile olmayan bir yardımcı sınıfın somutlaştırılması için "yeni" nin kullanılması kapsüllemeyi destekler. DI alternatifi, yardımcı sınıfı genel yapmak ve istemci sınıfına bir public kurucu veya ayarlayıcı eklemek olabilir; her iki değişiklik de orijinal yardımcı sınıf tarafından sağlanan kapsüllemeyi bozacaktır.
Rogério

1
"Bu iyi bir soru - ama bir noktada, eğer nesne bağımlılığını yerine getirecekse, en saf haliyle kapsüllemenin ihlal edilmesi gerekiyor" Cevabınızın temel dayanağı basitçe doğru değil. @ Rogério'nun bir bağımlılığı dahili olarak yenilediğini belirttiği gibi ve bir nesnenin kendi bağımlılıklarını dahili olarak tatmin ettiği diğer herhangi bir yöntem kapsüllemeyi ihlal etmez.
Neutrino

17

Bu, enjekte edilen bağımlılığı ortaya çıkarır ve OOP kapsülleme ilkesini ihlal eder.

Açıkçası, her şey kapsüllemeyi ihlal ediyor. :) İyi davranılması gereken bir tür ihale ilkesi.

Peki kapsüllemeyi ne ihlal ediyor?

Kalıtım var .

"Miras, bir alt sınıfı üst sınıf uygulamasının ayrıntılarına maruz bıraktığı için, genellikle 'mirasın kapsüllemeyi bozduğu' söylenir." (Dörtlü Çete 1995: 19)

Görünüşe yönelik programlama yapar . Örneğin, onMethodCall () geri aramasını kaydettirirsiniz ve bu size normal yöntem değerlendirmesine kod enjekte etmek, garip yan etkiler eklemek vb. İçin harika bir fırsat verir.

C ++ ' da arkadaş bildirimi yapar .

Ruby'de sınıf genişletme bunu yapar . Bir dize sınıfı tamamen tanımlandıktan sonra bir yerde bir dize yöntemini yeniden tanımlayın.

Pek çok şey yapar .

Kapsülleme iyi ve önemli bir ilkedir. Ama tek değil.

switch (principle)
{
      case encapsulation:
           if (there_is_a_reason)
      break!
}

3
"Bunlar benim ilkelerim ve eğer onları sevmezsen ... benim de başkaları var." (Groucho Marx)
Ron Inbar

2
Sanırım konu bu. Bağımlı enjeksiyona karşı kapsülleme. Bu nedenle, yalnızca önemli faydalar sağladığı durumlarda bağımlı enjeksiyon kullanın.
DI'ye

Bu cevabın ne söylemeye çalıştığından emin değilim ... DI yaparken kapsüllemeyi ihlal etmenin uygun olduğunu, "her zaman tamam" olduğunu çünkü her halükarda ihlal edileceğini veya yalnızca DI'nin kapsüllemeyi ihlal etmek için bir neden olabileceğini mi? Başka bir kayda göre, bu günlerde artık bağımlılıkları enjekte etmek için genel kuruculara veya özelliklere güvenmeye gerek yok ; bunun yerine, daha basit (daha az kod) ve kapsüllemeyi koruyan özel açıklamalı alanlara enjekte edebiliriz . Böylece her iki ilkeden aynı anda faydalanabiliriz.
Rogério

Kalıtım, ilke olarak kapsüllemeyi ihlal etmez, ancak ana sınıf kötü yazılmışsa işe yarayabilir. Ortaya çıkardığınız diğer noktalar, oldukça sınırlayıcı bir programlama paradigması ve mimari veya tasarımla pek ilgisi olmayan birkaç dil özelliğidir.
Neutrino

13

Evet, DI kapsüllemeyi ihlal ediyor ("bilgi gizleme" olarak da bilinir).

Ancak asıl sorun, geliştiriciler bunu KISS (Kısa ve Basit Tutun) ve YAGNI (İhtiyacınız Olmayacak) ilkelerini ihlal etmek için bir bahane olarak kullandıklarında ortaya çıkar.

Kişisel olarak basit ve etkili çözümleri tercih ediyorum. Durum bilgisi olan bağımlılıkları ne zaman ve nerede gerekli olursa olsun somutlaştırmak için çoğunlukla "new" operatörünü kullanıyorum. Basit, kapsüllü, anlaşılması ve test edilmesi kolay. Yani neden olmasın?


İleriyi düşünmek korkunç değil, ama Katılıyorum Basit Tutun, Aptal, özellikle İhtiyacınız Olmayacaksa! Geliştiricilerin döngüleri boşa harcadıklarını gördüm çünkü onlar bir şeyi ileriye dönük olarak çok fazla tasarlıyorlar ve buna yönelik önsezilere dayanıyorlar, bilinen / şüphelenilen iş gereksinimlerine bile.
Jacob McKay

5

İyi bir bağımlılık enjeksiyon kabı / sistemi, yapıcı enjeksiyonuna izin verecektir. Bağımlı nesneler kapsüllenecek ve herkese açık olması gerekmeyecektir. Dahası, bir DP sistemi kullanarak, kodlarınızdan hiçbiri, muhtemelen inşa edilmekte olan nesne de dahil olmak üzere, nesnenin nasıl inşa edildiğinin ayrıntılarını "bilmez". Bu durumda daha fazla kapsülleme vardır, çünkü kodunuzun neredeyse tamamı yalnızca kapsüllenmiş nesnelerin bilgisinden korunmakla kalmaz, aynı zamanda nesnelerin yapımına bile katılmaz.

Şimdi, yaratılan nesnenin kendi kapsüllenmiş nesnelerini, büyük olasılıkla yapıcısında yarattığı durumla karşılaştırdığınızı varsayıyorum. Benim DP anlayışım, bu sorumluluğu nesneden alıp başkasına vermek istediğimizdir. Bu amaçla, bu durumda DP konteyneri olan "başka biri", kapsüllemeyi "ihlal eden" samimi bilgiye sahiptir; faydası, bu bilgiyi nesneden, kendisinden çekmesidir. Birisinin ona sahip olması gerekiyor. Uygulamanızın geri kalanı yok.

Bunu şu şekilde düşünürdüm: Bağımlılık enjeksiyon kabı / sistemi kapsüllemeyi ihlal ediyor, ancak kodunuz bunu yapmıyor. Aslında, kodunuz her zamankinden daha "kapsüllenmiş".


3
İstemci nesnesinin bağımlılıklarını doğrudan somutlaştırabileceği bir durum varsa, o zaman neden bunu yapmayasınız? Kesinlikle yapılacak en basit şey bu ve test edilebilirliği mutlaka azaltmıyor. Sadeliğin ve daha iyi kapsüllemenin yanı sıra, bu durum aynı zamanda durum bilgisi olmayan tekler yerine durum bilgisi olan nesnelere sahip olmayı kolaylaştırır.
Rogério

1
@ Rogério'nun söylediği şeye ek olarak, potansiyel olarak önemli ölçüde daha verimli. Dünya tarihinde yaratılan her sınıfın, sahip olduğu nesnenin yaşam süresinin tamamı boyunca bağımlılıklarının her birinin somutlaştırılmasına gerek yoktu. DI kullanan bir nesne, kendi bağımlılıklarının en temel kontrolünü, yani yaşam sürelerini kaybeder.
Neutrino

5

Jeff Sternal'ın soruya yaptığı bir yorumda işaret ettiği gibi, cevap tamamen kapsüllemeyi nasıl tanımladığınıza bağlıdır .

Kapsüllemenin ne anlama geldiğine dair iki ana kamp var gibi görünüyor:

  1. Nesne ile ilgili her şey, bir nesne üzerindeki bir yöntemdir. Yani, bir Filenesne için yöntemler olabilir Save, Print, Display, ModifyText, vb
  2. Bir nesne kendi küçük dünyasıdır ve dış davranışa bağlı değildir.

Bu iki tanım birbiriyle doğrudan çelişmektedir. Bir Filenesne kendi kendini yazdırabiliyorsa, büyük ölçüde yazıcının davranışına bağlı olacaktır. Öte yandan, yalnızca kendisi için yazdırabilecek bir şeyi biliyorsa (bir IFilePrinterveya bu tür bir arayüz), Filenesnenin yazdırma hakkında hiçbir şey bilmesine gerek kalmaz ve bu nedenle onunla çalışmak nesneye daha az bağımlılık getirir.

Dolayısıyla, ilk tanımı kullanırsanız, bağımlılık enjeksiyonu kapsüllemeyi bozacaktır. Ama açıkçası ilk tanımı beğenip beğenmediğimi bilmiyorum - açıkça ölçeklenmiyor (eğer öyleyse, MS Word büyük bir sınıf olurdu).

Öte yandan, ikinci kapsülleme tanımını kullanıyorsanız , bağımlılık ekleme neredeyse zorunludur .


İlk tanım konusunda size kesinlikle katılıyorum. Ayrıca, programlamanın muhtemelen en büyük günahlarından biri olan ve muhtemelen ölçeklenmemesinin nedenlerinden biri olan SoC'yi de ihlal ediyor.
Marcus Stade

4

Kapsüllemeyi ihlal etmez. Bir işbirlikçi sağlıyorsunuz, ancak bunun nasıl kullanılacağına sınıf karar veriyor. Takip ettiğin sürece söyle, sorun yok sorma . Yapıcı enjeksiyonunu tercih edilir buluyorum, ancak ayarlayıcılar akıllı oldukları sürece iyi olabilirler. Yani sınıfın temsil ettiği değişmezleri sürdürmek için mantık içerirler.


1
Çünkü o ... değil mi? Bir kaydediciniz varsa ve günlükçüye ihtiyaç duyan bir sınıfınız varsa, günlükçüyü bu sınıfa geçirmek kapsüllemeyi ihlal etmez. Ve tüm bağımlılık enjeksiyonu budur.
jrockway

3
Bence kapsüllemeyi yanlış anladınız. Örneğin, saf bir randevu dersi alın. Dahili olarak gün, ay ve yıl örnek değişkenlerine sahip olabilir. Bunlar mantıksız basit ayarlayıcılar olarak ortaya çıkarılsaydı, bu kapsüllemeyi bozardı, çünkü ayı 2'ye ve günü 31'e ayarlamak gibi bir şey yapabilirdim. Öte yandan, ayarlayıcılar akıllıysa ve değişmezleri kontrol ederse, işler yolunda gider. . Ayrıca, ikinci sürümde, depolamayı 1/1/1970 tarihinden itibaren günler olacak şekilde değiştirebileceğimi ve gün / ay / yıl yöntemlerini uygun şekilde yeniden yazmam koşuluyla, arabirimi kullanan hiçbir şeyin bunun farkında olması gerekmediğini de unutmayın.
Jason Watkins

2
DI kesinlikle kapsüllemeyi / bilgi gizlemeyi ihlal ediyor. Özel bir iç bağımlılığı sınıfın genel arayüzünde açığa çıkan bir şeye dönüştürürseniz, tanım gereği bu bağımlılığın kapsüllenmesini bozmuş olursunuz.
Rogério

2
Kapsüllemenin DI tarafından tehlikeye atıldığını düşündüğüm somut bir örneğim var. DB'den "foo verileri" alan bir FooProvider'ım ve bunları önbelleğe alan ve sağlayıcının üstündeki şeyleri hesaplayan bir FooManager'ım var. Kodumun tüketicileri, yanlışlıkla veri almak için FooProvider'a gittiler, burada onu kapsüllemesini tercih ederdim, böylece yalnızca FooManager'dan haberdar olurlar. Bu temelde benim ilk sorum için tetikleyici.
urig

1
@Rogerio: Yapıcının yalnızca kompozisyon kökünde kullanıldığı için genel arayüzün bir parçası olmadığını iddia ediyorum . Bu nedenle, bağımlılık yalnızca bileşim kökü tarafından "görülür". Kompozisyon kökünün tek sorumluluğu, bu bağımlılıkları birbirine bağlamaktır. Bu nedenle, yapıcı enjeksiyonu kullanmak herhangi bir kapsüllemeyi bozmaz.
Jay Sullivan 13

4

Bu, olumlu oylanan cevaba benziyor ancak ben yüksek sesle düşünmek istiyorum - belki başkaları da olayları bu şekilde görüyor.

  • Klasik OO, sınıfın tüketicileri için genel "başlatma" sözleşmesini tanımlamak için yapıcıları kullanır (TÜM uygulama ayrıntılarını gizleme; diğer adıyla kapsülleme). Bu sözleşme, somutlaştırmadan sonra kullanıma hazır bir nesneye sahip olmanızı sağlayabilir (yani, kullanıcı tarafından hatırlanacak ek başlatma adımları (unutulmuş) yoktur).

  • (yapıcı) DI , bu genel kurucu arayüzü aracılığıyla uygulama detayını akıtarak kapsüllemeyi inkar edilemez şekilde kırar . Halen kamu kurucusunu kullanıcılar için başlatma sözleşmesini tanımlamakla sorumlu olarak gördüğümüz sürece, korkunç bir kapsülleme ihlali yarattık.

Teorik Örnek:

Sınıf Foo'nun 4 yöntemi vardır ve başlatma için bir tamsayıya ihtiyaç duyar, bu nedenle kurucusu Foo (int boyut) gibi görünür ve Foo sınıfı kullanıcıları için bir boyut sağlamaları gerektiği hemen anlaşılır. Foo'nun çalışması için başlatmada .

Diyelim ki bu Foo uygulamasının işini yapmak için bir IWidget'e ihtiyacı olabilir . Bu bağımlılığın yapıcı enjeksiyonu, bize benzer bir kurucu oluşturmamızı sağlar. Foo (int size, IWidget widget)

Beni bu konuda endişelendiren, şimdi başlatma verilerini bağımlılıklarla harmanlayan bir kurucumuz var - bir girdi sınıfın ( boyut ) kullanıcısını ilgilendiriyor , diğeri ise yalnızca kullanıcının kafasını karıştırmaya yarayan ve bir uygulama olan dahili bir bağımlılık. detay ( widget ).

Boyut parametresi bir bağımlılık DEĞİLDİR - basit bir örnek başına başlatma değeridir. IoC, harici bağımlılıklar için uygundur (widget gibi) ancak dahili durum başlatma için uygun değildir.

Daha da kötüsü, ya Widget bu sınıftaki 4 yöntemden yalnızca 2'si için gerekliyse; Kullanılmasa bile Widget için örnekleme ek yüküne maruz kalıyorum!

Bunu nasıl uzlaştırabilirim / uzlaştırabilirim?

Bir yaklaşım, operasyon sözleşmesini tanımlamak için özel olarak arayüzlere geçmektir; ve yapıcıların kullanıcılar tarafından kullanımını kaldırır. Tutarlı olması için, tüm nesnelere yalnızca arabirimler aracılığıyla erişilmesi ve yalnızca bazı çözümleyici biçimleriyle (bir IOC / DI konteyneri gibi) somutlaştırılması gerekir. Yalnızca kapsayıcı şeyleri somutlaştırabilir.

Bu Widget bağımlılığını halleder, ancak Foo arayüzünde ayrı bir başlatma yöntemine başvurmadan "boyutu" nasıl başlatabiliriz? Bu çözümü kullanarak, siz örneği aldığınızda bir Foo örneğinin tamamen başlatıldığından emin olma yeteneğimizi kaybettik. Bummer, çünkü yapıcı enjeksiyon fikrini ve basitliğini gerçekten seviyorum .

Başlatma YALNIZCA dış bağımlılıklardan DAHA FAZLA olduğunda, bu DI dünyasında garantili başlatmayı nasıl elde ederim?


Güncelleme: Unity 2.0'ın, çözümleme () sırasında bağımlılıkların IoC'si için normal mekanizmayı kullanırken, yapıcı parametreleri için değer sağlamayı desteklediğini (durum başlatıcıları gibi) henüz fark ettim. Belki başka kaplar da bunu destekliyor? Bu durum, init ve DI'yi bir yapıcıda karıştırmanın teknik zorluğunu çözer, ancak yine de kapsüllemeyi ihlal eder!
shawnT

Seni duyuyorum. Bu soruyu sordum çünkü ben de iki iyi şeyin (DI ve kapsülleme) biri diğerinin pahasına geldiğini hissediyorum. BTW, örneğinizde 4 yöntemden yalnızca 2'sinin IWidget'e ihtiyaç duyduğu durumda, bu diğer 2'nin farklı bir bileşen IMHO'ya ait olduğunu gösterir.
urig

3

Saf kapsülleme, asla elde edilemeyecek bir idealdir. Tüm bağımlılıklar gizli olsaydı, DI'ye hiç ihtiyacınız olmazdı. Şöyle düşünün, örneğin bir araba nesnesinin hızının tamsayı değeri gibi nesne içinde içselleştirilebilecek gerçekten özel değerlere sahipseniz, o zaman harici bağımlılığınız yoktur ve bu bağımlılığı tersine çevirmenize veya enjekte etmenize gerek kalmaz. Tamamen özel işlevler tarafından çalıştırılan bu tür iç durum değerleri, her zaman kapsüllemek istediğiniz şeylerdir.

Ancak, belirli bir tür motor nesnesi isteyen bir araba inşa ediyorsanız, o zaman bir dış bağımlılığınız vardır. Ya bu motoru - örneğin yeni GMOverHeadCamEngine () - dahili olarak araba nesnesinin yapıcısı içinde somutlaştırabilir, kapsüllemeyi koruyabilir, ancak somut bir GMOverHeadCamEngine sınıfına çok daha sinsi bir bağlantı oluşturabilir veya onu enjekte ederek Car nesnenizin çalışmasına izin verebilirsiniz. agnostik olarak (ve çok daha güçlü bir şekilde) örneğin somut bağımlılık olmadan bir arayüz IEngine üzerinde. Bunu başarmak için bir IOC konteyneri veya basit bir DI kullanmanız önemli değil - mesele şu ki, herhangi birine bağlanmadan birçok motor türünü kullanabilen bir Arabanız var, böylece kod tabanınızı daha esnek hale getiriyor ve yan etkilere daha az eğilimli.

DI, bir kapsülleme ihlali değildir, neredeyse her OOP projesinde kuşkusuz kapsülleme zorunlu olarak kırıldığında bağlantıyı en aza indirmenin bir yoludur. Bir arayüze harici olarak bir bağımlılık enjekte etmek, birleştirme yan etkilerini en aza indirir ve sınıflarınızın uygulama konusunda agnostik kalmasına izin verir.


3

Bağımlılığın gerçekten bir uygulama ayrıntısı mı yoksa müşterinin bir şekilde bilmek isteyeceği / bilmek isteyeceği bir şey mi olduğuna bağlıdır. Alakalı olan bir şey, sınıfın hedeflediği soyutlama düzeyidir. İşte bazı örnekler:

Aramaları hızlandırmak için kaputun altında önbelleğe almayı kullanan bir yönteminiz varsa, önbellek nesnesi bir Singleton veya başka bir şey olmalı ve enjekte edilmemelidir . Önbelleğin hiç kullanılıyor olması, sınıfınızın istemcilerinin önemsememesi gereken bir uygulama ayrıntısıdır.

Sınıfınızın veri akışlarının çıktısını alması gerekiyorsa, muhtemelen çıktı akışını enjekte etmek mantıklıdır, böylece sınıf sonuçları bir diziye, bir dosyaya veya başka birinin verileri göndermek isteyebileceği herhangi bir yere kolayca çıkarabilir.

Gri bir alan için, monte carlo simülasyonu yapan bir sınıfınız olduğunu varsayalım. Bir rastgelelik kaynağına ihtiyacı var. Bir yandan, buna ihtiyaç duyduğu gerçeği, müşterinin rastgeleliğin tam olarak nereden geldiğini gerçekten umursamadığı bir uygulama detayıdır. Öte yandan, gerçek dünyadaki rastgele sayı üreteçleri, müşterinin kontrol etmek isteyebileceği rasgelelik derecesi, hız vb. Arasında değiş tokuş yaptığından ve müşteri tekrarlanabilir davranış elde etmek için tohumlamayı kontrol etmek isteyebileceğinden, enjeksiyon mantıklı olabilir. Bu durumda, rastgele bir sayı üreteci belirtmeden sınıfı oluşturmanın bir yolunu sunmayı ve varsayılan olarak iş parçacığı yerel bir Singleton kullanmayı öneririm. Daha ince kontrol ihtiyacı ortaya çıkarsa / ortaya çıktığında, rastgele bir kaynağın enjekte edilmesine izin veren başka bir kurucu sağlayın.


2

Sadeliğe inanıyorum. Domain sınıflarında IOC / Dependecy Enjeksiyonunun uygulanması, ilişkiyi açıklayan harici bir xml dosyalarına sahip olarak kodun ana hatlarını çok daha zor hale getirmesi dışında herhangi bir iyileştirme yapmaz. EJB 1.0 / 2.0 ve struts 1.1 gibi birçok teknoloji, XML'e konulan şeyleri azaltarak geri dönüyor ve bunları açıklama olarak koda koymayı deniyor. Bu nedenle, geliştirdiğiniz tüm sınıflar için IOC uygulamak kodu anlamsız hale getirecektir.

IOC, bağımlı nesne derleme zamanında oluşturulmaya hazır olmadığında fayda sağlar. Bu, altyapı soyut düzey mimari bileşenlerinin çoğunda gerçekleşebilir ve farklı senaryolar için çalışması gerekebilecek ortak bir temel çerçeve oluşturmaya çalışır. Bu yerlerde IOC kullanımı daha mantıklı. Yine de bu, kodu daha basit / bakımı yapılabilir hale getirmez.

Diğer tüm teknolojiler gibi, bu da PRO'lara ve Eksilere sahiptir. Endişem, en iyi bağlam kullanımlarına bakılmaksızın her yerde en son teknolojileri uyguluyoruz.


2

Kapsülleme, yalnızca bir sınıfın hem nesneyi yaratma sorumluluğu varsa (bu, uygulama ayrıntılarının bilinmesini gerektirir) hem de sınıfı kullanıyorsa (bu ayrıntılar hakkında bilgi gerektirmeyen) bozulur. Nedenini açıklayacağım, ancak önce hızlı bir araba anaolojisi:

Eski 1971 Kombi'mi sürerken, gaza basabiliyordum ve (biraz) daha hızlı gitti. Nedenini bilmeme gerek yoktu ama fabrikada Kombi'yi yapan adamlar nedenini tam olarak biliyorlardı.

Ama kodlamaya geri dönelim. Kapsülleme , "bu uygulamayı kullanan bir şeyden bir uygulama ayrıntısını gizlemektir." Kapsülleme iyi bir şeydir çünkü uygulama ayrıntıları sınıfın kullanıcısı bilmeden değişebilir.

Bağımlılık enjeksiyonu kullanılırken, yapıcı enjeksiyonu, servis türü nesneleri oluşturmak için kullanılır (durumu modelleyen varlık / değer nesnelerinin aksine). Hizmet türü nesnesindeki herhangi bir üye değişken, dışarı sızmaması gereken uygulama ayrıntılarını temsil eder. örneğin soket bağlantı noktası numarası, veritabanı kimlik bilgileri, şifreleme gerçekleştirmek için çağrılacak başka bir sınıf, bir önbellek vb.

Yapıcı sınıfı başlangıçta oluşturulurken zaman geçerlidir. Bu, inşaat aşamasında, DI kabınız (veya fabrika) tüm servis nesnelerini birbirine bağlarken gerçekleşir. DI kapsayıcısı yalnızca uygulama ayrıntılarını bilir. Kombi fabrikasındaki adamların bujiler hakkında bilgisi olduğu gibi, uygulama detaylarıyla ilgili her şeyi bilir.

Çalışma zamanında, yaratılan hizmet nesnesine gerçek bir iş yapmak için apon denir. Şu anda, nesneyi arayan, uygulama ayrıntılarıyla ilgili hiçbir şey bilmiyor.

Kombi'mi sahile ben sürüyorum.

Şimdi, kapsüllemeye geri dönelim. Uygulama ayrıntıları değişirse, çalışma zamanında bu uygulamayı kullanan sınıfın değişmesi gerekmez. Kapsülleme bozuk değil.

Yeni arabamı sahile de sürebilirim. Kapsülleme bozuk değil.

Uygulama ayrıntıları değişirse, DI kapsayıcısının (veya fabrikanın) değiştirilmesi gerekir. İlk etapta fabrikadan uygulama detaylarını saklamaya çalışmadınız.


Fabrikanızı nasıl test edersiniz? Ve bu, müşterinin çalışan bir araba almak için fabrika hakkında bilgi sahibi olması gerektiği anlamına gelir, bu da sisteminizdeki diğer nesneler için bir fabrikaya ihtiyacınız olduğu anlamına gelir.
Rodrigo Ruiz

2

Konuyla biraz daha fazla mücadele ettikten sonra, Bağımlılık Enjeksiyonunun (şu anda) kapsüllemeyi bir dereceye kadar ihlal ettiği görüşündeyim. Yine de beni yanlış anlamayın - Bağımlılık enjeksiyonu kullanmanın çoğu durumda ödün vermeye değer olduğunu düşünüyorum.

DI'nin kapsüllemeyi neden ihlal ettiğinin durumu, üzerinde çalıştığınız bileşen "harici" bir tarafa teslim edilecek olduğunda netleşir (bir müşteri için bir kitaplık yazmayı düşünün).

Bileşenim, alt bileşenlerin kurucu (veya kamusal mülkler) aracılığıyla enjekte edilmesini gerektirdiğinde,

"kullanıcıların bileşenin dahili verilerini geçersiz veya tutarsız bir duruma ayarlamasını önleme".

Aynı zamanda söylenemez ki

"Bileşenin kullanıcılarının (diğer yazılım parçaları) yalnızca bileşenin ne yaptığını bilmeleri gerekir ve kendilerini nasıl yaptığının ayrıntılarına bağımlı hale getiremezler . "

Her iki alıntı da Wikipedia'dan .

Spesifik bir örnek vermek gerekirse: Bir WCF hizmetiyle (esasen uzak bir cephe) iletişimi basitleştiren ve gizleyen bir istemci tarafı DLL sağlamam gerekiyor. 3 farklı WCF proxy sınıfına bağlı olduğundan, DI yaklaşımını kullanırsam, bunları kurucu aracılığıyla göstermek zorunda kalıyorum. Bununla birlikte, gizlemeye çalıştığım iletişim katmanımın içini açığa çıkarıyorum.

Genelde ben DI içinim. Bu özel (aşırı) örnekte, beni tehlikeli olarak görüyor.


2

DI, Paylaşılmayan nesneler için Kapsüllemeyi ihlal ediyor - nokta. Paylaşılan nesnelerin, yaratılan nesnenin dışında bir ömrü vardır ve bu nedenle, yaratılan nesnede TOPLANMALIDIR. Yaratılan nesneye özel olan nesneler, oluşturulan nesneye OLUŞTURULMALIDIR - oluşturulan nesne yok edildiğinde, oluşturulan nesneyi de beraberinde alır. İnsan vücudunu örnek alalım. Neler oluşturulur ve bir araya getirilir. DI kullanacak olsaydık, insan vücudu yapıcısının 100'lerce nesnesi olurdu. Örneğin organların çoğu (potansiyel olarak) değiştirilebilir. Ama yine de vücutta birleşiyorlar. Kan hücreleri, vücutta her gün dış etkilere (protein dışında) ihtiyaç duyulmadan oluşturulur (ve yok edilir). Böylece, kan hücreleri vücut tarafından dahili olarak oluşturulur - yeni BloodCell ().

DI savunucuları, bir nesnenin ASLA yeni operatörü kullanmaması gerektiğini savunurlar. Bu "saf" yaklaşım yalnızca kapsüllemeyi değil, aynı zamanda nesneyi yaratan kişi için Liskov İkame İlkesini de ihlal eder.


1

Ben de bu kavramla mücadele ettim. İlk başta, bir nesneyi somutlaştırmak için DI kapsayıcısını (Yay gibi) kullanma 'gerekliliği', çemberlerden atlama gibi hissettirdi. Ama gerçekte, bu gerçekten bir çember değil - ihtiyacım olan nesneleri yaratmanın başka bir "yayınlanmış" yolu. Elbette, kapsülleme 'bozuktur' çünkü 'sınıfın dışındaki' biri neye ihtiyacı olduğunu bilir, ancak bunu gerçekten bilen sistemin geri kalanı değildir - bu DI kabıdır. Sihirli hiçbir şey farklı olmaz çünkü DI bir nesnenin diğerine ihtiyacı olduğunu 'bilir'.

Aslında daha da iyi hale geliyor - Fabrikalar ve Depolara odaklanarak DI'nin dahil olduğunu bilmeme bile gerek yok! Bu benim için kapağı tekrar kapsüllemeye koyuyor. Whew!


1
DI, tüm örnekleme zincirinden sorumlu olduğu sürece, kapsüllemenin bir tür meydana geldiğine katılıyorum. Bir bakıma, çünkü bağımlılıklar hala halka açık ve kötüye kullanılabilir. Ancak zincirin "yukarısında" bir yerde DI kullanmadan bir nesnenin somutlaştırılması gerektiğinde (belki de "üçüncü taraf" olabilirler), o zaman karmaşıklaşır. Bağımlılıklarınıza maruz kalıyorlar ve onları kötüye kullanma eğiliminde olabilirler. Ya da onlar hakkında hiçbir şey bilmek istemeyebilirler.
urig

1

PS. Sağlayarak Bağımlılık Injection Eğer ille yok kırmak Kapsüllenmesi'nin . Misal:

obj.inject_dependency(  factory.get_instance_of_unknown_class(x)  );

Müşteri kodu, uygulama ayrıntılarını hala bilmiyor.


Örneğinizde herhangi bir şey nasıl enjekte edilir? (Ayarlayıcı işlevinizin adlandırılması hariç)
foo

1

Belki bu, onun hakkında saf bir düşünme şeklidir, ancak bir tamsayı parametresini alan bir kurucu ile bir hizmeti parametre olarak alan bir kurucu arasındaki fark nedir? Bu, yeni nesnenin dışında bir tamsayı tanımlamanın ve onu nesneye beslemenin kapsüllemeyi bozduğu anlamına mı geliyor? Hizmet yalnızca yeni nesne içinde kullanılıyorsa, bunun kapsüllemeyi nasıl bozacağını anlamıyorum.

Ayrıca, bir tür otomatik kablolama özelliğini kullanarak (örneğin, C # için Autofac) kodu son derece temiz hale getirir. Autofac oluşturucu için genişletme yöntemleri oluşturarak, bağımlılıklar listesi büyüdükçe zaman içinde korumak zorunda kalacağım çok sayıda DI yapılandırma kodunu kesebildim.


Değer ve hizmetle ilgili değil. Bu, kontrolün tersine çevrilmesi ile ilgilidir - sınıfın kurucusunun sınıfı ayarlaması veya sınıfın kurulmasını bu hizmetin devralması. Bu sınıfın uygulama ayrıntılarını bilmesi gerektiği için, bakımını yapmak ve senkronize etmek için başka bir yeriniz olur.
foo

1

En azından DI'nin kapsüllemeyi önemli ölçüde zayıflattığı apaçık ortada. Buna ek olarak, burada DI'nin göz önünde bulundurulması gereken diğer bazı dezavantajları var.

  1. Kodun yeniden kullanımını zorlaştırır. Bir istemcinin açıkça bağımlılık sağlamak zorunda kalmadan kullanabileceği bir modülün kullanımı, istemcinin bir şekilde bu bileşenin bağımlılıklarının ne olduğunu keşfetmesi ve sonra bir şekilde bunları kullanılabilir hale getirmesi gereken bir modülden daha kolaydır. Örneğin, bir ASP uygulamasında kullanılmak üzere orijinal olarak oluşturulan bir bileşen, bağımlılıklarının, istemci http istekleriyle ilgili yaşam sürelerine sahip nesne örnekleri sağlayan bir DI kapsayıcısı tarafından sağlanmasını bekleyebilir. Bu, orijinal ASP uygulamasıyla aynı dahili DI kapsayıcısına sahip olmayan başka bir istemcide yeniden üretilmesi kolay olmayabilir.

  2. Kodu daha kırılgan hale getirebilir. Arayüz belirtimiyle sağlanan bağımlılıklar beklenmedik şekillerde uygulanabilir ve bu da statik olarak çözülmüş somut bir bağımlılıkla mümkün olmayan bütün bir çalışma zamanı hatası sınıfına yol açar.

  3. Nasıl çalışmasını istediğiniz konusunda daha az seçeneğiniz olması anlamında kodu daha az esnek hale getirebilir. Her sınıfın sahip olduğu örneğin tüm ömrü boyunca tüm bağımlılıklarına sahip olması gerekmez, ancak birçok DI uygulamasında başka seçeneğiniz yoktur.

Bunu akılda tutarak, bence en önemli soru, " belirli bir bağımlılığın dışarıdan belirtilmesi gerekiyor mu? " Uygulamada, sadece testi desteklemek için harici olarak sağlanan bir bağımlılık yapmayı nadiren gerekli buldum.

Bir bağımlılığın gerçekten dışarıdan sağlanması gerektiğinde, bu normalde nesneler arasındaki ilişkinin bir iç bağımlılıktan ziyade bir işbirliği olduğunu düşündürür; bu durumda uygun hedef, bir sınıfın diğerinin içinde kapsüllenmesinden ziyade her bir sınıfın kapsüllenmesidir. .

Tecrübelerime göre, DI kullanımıyla ilgili temel sorun, yerleşik DI ile bir uygulama çerçevesi ile başlamanız veya kod tabanınıza DI desteği eklemenizdir, bazı nedenlerden dolayı insanlar, DI desteğine sahip olduğunuz için bunun doğru olması gerektiğini varsayarlar. her şeyi somutlaştırmanın yolu . "Bu bağımlılığın harici olarak belirtilmesi gerekiyor mu?" Sorusunu sorma zahmetine bile girmezler. Daha da kötüsü, diğer herkesi de DI desteğini her şey için kullanmaya zorlamaya başlarlar .

Bunun sonucu, kaçınılmaz bir şekilde kod tabanınızın, kod tabanınızda herhangi bir şeyin herhangi bir örneğini yaratmanın geniş kapsamlı DI kapsayıcı yapılandırması yığınları gerektirdiği ve herhangi bir şeyin hata ayıklamasının iki kat daha zor olduğu bir duruma dönüşmeye başlamasıdır. ve herhangi bir şeyin somutlaştırıldığı yer.

Yani soruya cevabım şu. DI'yi, sizin için çözdüğü ve başka hiçbir şekilde daha basit bir şekilde çözemeyeceğiniz gerçek bir sorunu tanımlayabileceğiniz yerde kullanın.


0

Aşırı bir şekilde alındığında, DI'nin kapsüllemeyi ihlal edebileceğine katılıyorum. Genellikle DI, asla tam anlamıyla kapsüllenmemiş bağımlılıkları ortaya çıkarır. İşte Miško Hevery'nin Tekilleri Patolojik Yalancılar'dan ödünç alınan basitleştirilmiş bir örnek :

Bir CreditCard testi ile başlarsınız ve basit bir birim testi yazarsınız.

@Test
public void creditCard_Charge()
{
    CreditCard c = new CreditCard("1234 5678 9012 3456", 5, 2008);
    c.charge(100);
}

Önümüzdeki ay 100 dolarlık bir fatura alacaksın. Neden suçlandın? Birim testi, bir üretim veritabanını etkiledi. Dahili olarak, CreditCard arar Database.getInstance(). CreditCard'ı DatabaseInterfacekurucusunda bir alacak şekilde yeniden düzenlemek, bağımlılık olduğu gerçeğini ortaya çıkarır. Ancak, CreditCard sınıfı dışarıdan görülebilen yan etkilere neden olduğundan, bağımlılığın başlangıçta asla kapsüllenmediğini iddia ediyorum. CreditCard'ı yeniden düzenleme yapmadan test etmek istiyorsanız, bağımlılığı kesinlikle gözlemleyebilirsiniz.

@Before
public void setUp()
{
    Database.setInstance(new MockDatabase());
}

@After
public void tearDown()
{
    Database.resetInstance();
}

Veritabanını bir bağımlılık olarak ortaya çıkarmanın kapsüllemeyi azaltıp azaltmayacağı konusunda endişelenmeye değeceğini sanmıyorum, çünkü bu iyi bir tasarım. Tüm DI kararları o kadar basit olmayacak. Ancak, diğer cevapların hiçbiri karşı bir örnek göstermiyor.


1
Ünite testleri genellikle sınıf yazarı tarafından yazılır, bu nedenle test senaryosundaki bağımlılıkları teknik bir bakış açısıyla açıklamakta sorun yoktur. Daha sonra kredi kartı sınıfı PayPal gibi bir Web API'sini kullanmak için değiştiğinde, kullanıcının Öldürülmesi durumunda her şeyi değiştirmesi gerekecektir. Birim testleri genellikle test edilen konu hakkında derinlemesine bilgiyle yapılır (asıl mesele bu değil mi?) Bu yüzden testlerin tipik bir örnekten çok bir istisna olduğunu düşünüyorum.
kizzx2

1
DI'nin amacı, tanımladığınız değişikliklerden kaçınmaktır. Bir Web API'sinden PayPal'a geçtiyseniz, çoğu testi değiştirmezsiniz çünkü bunlar bir MockPaymentService kullanır ve CreditCard bir PaymentService ile oluşturulur. CreditCard ile gerçek bir PaymentService arasındaki gerçek etkileşime bakan bir avuç testiniz olacak, bu nedenle gelecekteki değişiklikler oldukça izole olacak. Daha derin bağımlılık grafikleri için faydalar daha da büyüktür (örneğin, CreditCard'a bağlı bir sınıf).
Craig P. Motlin

@Craig s. Motlin CreditCardNesne, sınıfın dışında herhangi bir şey değişmeden bir WebAPI'den PayPal'a nasıl değişebilir?
Ian Boyd

@Ian, CreditCard'ın yapıcısında DatabaseInterfaces uygulamalarındaki değişikliklerden onu koruyan bir DatabaseInterface almak için yeniden düzenlenmesi gerektiğini söyledim. Belki bunun daha genel olması ve WebAPI'nin başka bir uygulama olabileceği bir StorageInterface alması gerekiyor. Ancak PayPal yanlış düzeyde, çünkü Kredi Kartı'na bir alternatif. Hem PayPal hem de CreditCard, uygulamanın diğer bölümlerini bu örneğin dışından korumak için PaymentInterface'i uygulayabilir.
Craig P. Motlin

@ kizzx2 Diğer bir deyişle, bir kredi kartının PayPal kullanması gerektiğini söylemek saçmadır. Gerçek hayatta alternatiflerdir.
Craig P. Motlin

0

Bunun bir kapsam meselesi olduğunu düşünüyorum. Kapsüllemeyi tanımladığınızda (nasıl yapılacağını bildirmeden), kapsüllenmiş işlevselliğin ne olduğunu tanımlamalısınız.

  1. Sınıf olduğu gibi : Kapsadığın şey, sınıfın tek sorumluluğu. Nasıl yapılacağını biliyor. Örnek olarak, sıralama. Sipariş için bir karşılaştırıcı enjekte ederseniz, diyelim ki müşteriler, bu kapsüllenmiş şeyin bir parçası değil: hızlı sıralama.

  2. Yapılandırılmış işlevsellik : kullanıma hazır bir işlevsellik sağlamak istiyorsanız, QuickSort sınıfını değil, Comparator ile yapılandırılmış QuickSort sınıfının bir örneğini sağlamış olursunuz. Bu durumda, oluşturma ve yapılandırmadan sorumlu kod, kullanıcı kodundan gizlenmelidir. Ve bu kapsülleme.

Sınıfları programlarken, tek sorumlulukları sınıflara uyguluyorsunuz, 1. seçeneği kullanıyorsunuz.

Uygulamaları programlarken, bu, bazı yararlı somut işler üstlenen bir şey yapmaktır, o zaman tekrar tekrar 2. seçeneği kullanırsınız.

Bu, yapılandırılmış örneğin uygulamasıdır:

<bean id="clientSorter" class="QuickSort">
   <property name="comparator">
      <bean class="ClientComparator"/>
   </property>
</bean>

Başka bir müşteri kodu bunu şu şekilde kullanır:

<bean id="clientService" class"...">
   <property name="sorter" ref="clientSorter"/>
</bean>

Kapsüllüdür çünkü uygulamayı değiştirirseniz ( clientSorterfasulye tanımını değiştirirseniz ) istemci kullanımını bozmaz. Belki, hepsi birlikte yazılmış xml dosyalarını kullandığınızda tüm ayrıntıları görüyorsunuz. Ama inanın bana, müşteri kodu ( ClientService) sıralayıcı hakkında hiçbir şey bilmiyor .


0

Muhtemelen Encapsulationbiraz perspektife bağlı olduğundan bahsetmeye değer .

public class A { 
    private B b;

    public A() {
        this.b = new B();
    }
}


public class A { 
    private B b;

    public A(B b) {
        this.b = b;
    }
}

ASınıfta çalışan birinin bakış açısından , ikinci örnekte , sınıfın Adoğası hakkında çok daha az şey biliyorthis.b

DI olmadan

new A()

vs

new A(new B())

Bu koda bakan kişi A, ikinci örnekteki doğası hakkında daha fazla şey biliyor .

DI ile, en azından sızdırılan bilgilerin tamamı tek bir yerde.

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.