Neredeyse herkesin ortak bir veri yapısına erişmesi gerektiğinde bağımlılık enjeksiyonunun faydaları nelerdir?


20

OOP'ta küresellerin kötü olmasının birçok nedeni var .

Paylaşılması gereken nesnelerin sayısı veya boyutu işlev parametrelerinde verimli bir şekilde iletilemeyecek kadar büyükse, genellikle herkes global bir nesne yerine Bağımlılık Enjeksiyonu önerir .

Ancak, neredeyse herkesin belirli bir veri yapısı hakkında bilgi sahibi olması durumunda, Bağımlılık Enjeksiyonu neden küresel bir nesneden daha iyi?

Örnek (basit bir uygulama, belirli bir uygulamada çok fazla derinlemesine inceleme yapmadan genel olarak noktayı göstermek için)

Tür, isim, renk, hız, konum vb. Gibi çok sayıda özelliğe ve duruma sahip bir dizi sanal araç vardır. Bir dizi kullanıcı bunları uzaktan kontrol edebilir ve çok sayıda olay (hem kullanıcı- başlatılmış ve otomatik) durumlarının veya özelliklerinin çoğunu değiştirebilir.

Saf çözüm, sadece küresel bir kap yapmak,

vector<Vehicle> vehicles;

her yerden erişilebilir.

Daha fazla OOP dostu çözüm, konteynerin ana olay döngüsünü işleyen sınıfın üyesi olması ve yapıcısında somutlaştırılması olacaktır. İhtiyacı olan ve ana iş parçacığının üyesi olan her sınıfa, yapıcılarındaki bir işaretçi aracılığıyla kaba erişim verilecektir. Örneğin, bir ağ bağlantısı üzerinden harici bir mesaj gelirse, ayrıştırmayı işleyen bir sınıf (her bağlantı için bir tane) devralınır ve ayrıştırıcı kapsayıcıya bir işaretçi veya başvuru yoluyla erişebilir. Şimdi, ayrıştırılan mesaj kabın bir öğesinde bir değişiklikle sonuçlanırsa veya bir eylemi gerçekleştirmek için dışarıda bazı veriler gerektirirse, sinyaller ve yuvalar (veya daha kötüsü, daha sonra ayrıştırıcıyı çağıran tarafından alınması için bunları ayrıştırıcıda saklamak). Tabii ki, bağımlılık enjeksiyonu yoluyla kaba erişim alan tüm sınıflar aynı iş parçacığının bir parçasıdır. Farklı iş parçacıkları doğrudan erişemez, ancak işlerini yapar ve daha sonra ana iş parçacığına sinyal gönderir ve ana iş parçasındaki yuvalar kapsayıcıyı güncelleştirir.

Bununla birlikte, eğer sınıfların çoğunluğu konteynere erişecekse, onu küreselden gerçekten farklı kılan nedir? Eğer çok fazla sınıfın kapsayıcıdaki verilere ihtiyacı varsa, "bağımlılık enjeksiyon yolu" sadece gizlenmiş bir küresel değil mi?

Bir cevap iş parçacığı güvenliği olacaktır: Küresel kapsayıcıyı kötüye kullanmamaya dikkat etsem de, belki de yakın bir son tarih baskısı altında gelecekte başka bir geliştirici, küresel kapsayıcıyı, hepsine bakmadan farklı bir iş parçacığında kullanacaktır. çarpışma olayları. Bununla birlikte, bağımlılık enjeksiyonu durumunda bile, başka bir iş parçacığında çalışan birine bir işaretçi verebilir ve aynı sorunlara yol açabilir.


6
Bekle, değişmez küresellerden bahsediyorsun ve bunu haklı çıkarmak için "küresel devlet neden kötü?" O NE LAN?
Telastyn

1
Ben küreselleri haklı çıkarmıyorum. Bence küresellerin iyi bir çözüm olmadığı gerçeğine katılıyorum. Sadece bağımlılık enjeksiyonunu kullanmanın bile globallerden daha iyi (veya belki de neredeyse ayırt edilemez) olduğu bir durumdan bahsediyorum. Bu nedenle bir cevap, bu durumda hala bağımlılık enjeksiyonunu kullanmanın diğer gizli faydalarına işaret edebilir veya diğer yaklaşımların di
vsz

9
Ama bir var karar salt okunur genel ya da küresel devlet arasındaki fark.
Telastyn


1
@vsz gerçekten değil - soru, birçok farklı nesnenin problemi etrafında bir yol olarak servis bulucu fabrikaları hakkında; ancak yanıtları, sınıflara aktarılan küresel veriler yerine sınıfların global verilere erişmesine izin verme sorunuzu da yanıtlar.
gbjbaanb

Yanıtlar:


37

neredeyse herkesin belirli bir veri yapısı hakkında bilgi sahibi olması durumunda, Bağımlılık Enjeksiyonu neden küresel bir nesneden daha iyi?

Bağımlılık enjeksiyonu dilimlenmiş ekmeklerden bu yana en iyi şeydir , küresel nesneler onlarca yıldır tüm kötülüklerin kaynağı olduğu bilinmektedir , bu yüzden bu oldukça ilginç bir sorudur.

Bağımlılık enjeksiyonu sadece bir kaynağa ihtiyaç duyan her aktörün buna sahip olmasını sağlamak değildir , çünkü açıkçası, eğer tüm kaynakları global yaparsanız, her aktörün her kaynağa erişimi olacaktır, problem çözüldü, değil mi?

Bağımlılık enjeksiyonu noktası:

  1. Oyuncuların ihtiyaçlara göre kaynaklara erişmesine izin vermek ve
  2. Herhangi bir aktör tarafından bir kaynağın hangi örneğine erişildiğini kontrol etmek .

Özel yapılandırmanızda tüm aktörlerin aynı kaynak örneğine erişmesi gerektiği gerçeği önemsizdir. Güven bana, bir gün şeyleri yeniden yapılandırma ihtiyacına sahip olacaksınız, böylece aktörler kaynağın farklı örneklerine erişebilecek ve daha sonra kendinizi bir köşede boyadığınızı anlayacaksınız. Bazı cevaplar zaten böyle bir konfigürasyona işaret etti: test etme .

Başka bir örnek: uygulamanızı istemci-sunucuya böldüğünüzü varsayalım. İstemcideki tüm aktörler, istemci üzerinde aynı merkezi kaynak kümesini kullanır ve sunucudaki tüm aktörler, sunucudaki aynı merkezi kaynak kümesini kullanır. Şimdi, bir gün, istemci-sunucu uygulamanızın "bağımsız" bir sürümünü oluşturmaya karar verdiğinizi varsayalım, hem istemcinin hem de sunucunun tek bir yürütülebilir dosyada paketlendiği ve aynı sanal makinede çalıştırıldığı. (Veya seçtiğiniz dile bağlı olarak çalışma zamanı ortamı.)

Bağımlılık enjeksiyonu kullanıyorsanız, tüm sunucu aktörlerine sunucu kaynak örneklerini alırken, tüm istemci aktörlerine çalışmak için istemci kaynak örneklerinin verildiğinden emin olabilirsiniz.

Bağımlılık enjeksiyonu kullanmazsanız, bir sanal makinede her kaynağın yalnızca bir global örneği bulunabileceğinden tamamen şansınız kalmaz.

O zaman şunu düşünmelisiniz: tüm aktörlerin bu kaynağa gerçekten erişmesi gerekiyor mu? Gerçekten mi?

Bu kaynağı bir tanrı nesnesine dönüştürme hatasını yapmış olabilirsiniz (yani, elbette herkesin ona erişmesi gerekir) veya belki de projenizde o kaynağa erişmesi gereken aktörlerin sayısını çok fazla tahmin ediyorsunuzdur. .

Global kullanıcılarla, uygulamanızın her bir kaynak kodu satırının her bir global kaynağa erişimi vardır. Bağımlılık enjeksiyonu ile, her kaynak örneği yalnızca gerçekten ihtiyaç duyan aktörler tarafından görülebilir. İkisi aynıysa (belirli bir kaynağa ihtiyaç duyan aktörler, projenizdeki kaynak kodu satırlarının% 100'ünü içerir), o zaman tasarımınızda bir hata yapmış olmanız gerekir. Bu yüzden ya

  • Büyük büyük dev tanrı kaynağını daha küçük alt kaynaklara dönüştüren refactor, bu yüzden farklı aktörlerin farklı parçalarına erişmesi gerekiyor, ancak nadiren bir aktörün tüm parçalarına ihtiyacı var veya

  • Aktörlerinizi sırayla sadece üzerinde çalışmaları gereken sorunun alt kümeleri olarak parametre olarak kabul edin, bu yüzden her zaman büyük bir büyük büyük merkezi kaynağa danışmak zorunda kalmazlar.


Bunu anladığımdan emin değilim - bir yandan DI'nin bir kaynağın birden fazla örneğini kullanmanıza izin verdiğini ve küresellerin kullanmadıklarını söylüyorsunuz, tek bir statik değişkeni çoğaltılmış nesnelere karşı karıştırmadıkça açıkça saçma - bir sebep yok "global" tek bir örnek veya tek bir sınıf olmalıdır.
gbjbaanb

3
@gbjbaanb Kaynağın bir Zork olduğunu varsayalım. OP, sistemindeki her bir nesnenin çalışmak için bir Zork'a ihtiyacı olmadığını, aynı zamanda sadece bir Zork'un var olacağını ve tüm nesnelerinin sadece bir Zork örneğine erişmesi gerektiğini söylüyor. Bu iki varsayımdan hiçbirinin makul olmadığını söylüyorum: muhtemelen tüm nesnelerinin bir Zork'a ihtiyacı olduğu doğru değildir ve bazı nesnelerin belirli bir Zork örneğine ihtiyaç duyduğu zamanlar olacaktır, diğer nesneler ise farklı Zork örneği.
Mike Nakis

2
@gbjbaanb İki küresel Zork örneğine sahip olmanın, aptalca neden ZorkA ve ZorkB adını vermenin ve birinin ZorkA kullanacağı ve hangisinin ZorkB kullanacağını açık kodlamamı açıklamanızı istediğini düşünmüyorum. sağ?
Mike Nakis

1
Herkesi söylemedim, neredeyse herkesi söyledim . Sorunun amacı, DI'nin ihtiyaç duymayan birkaç aktörden erişimi reddetmenin yanı sıra başka faydaları olup olmadığıydı. "Tanrı nesneleri" nin bir anti-desen olduğunu biliyorum, ancak körü körüne en iyi uygulamaları takip etmek de bir tane olabilir. Bir programın tüm amacı belirli bir kaynakla çeşitli şeyler yapmak olabilir, bu durumda neredeyse herkesin bu kaynağa erişmesi gerekir. Buna iyi bir örnek, bir görüntü üzerinde çalışan bir program olabilir: içindeki neredeyse her şeyin görüntü verileriyle ilgisi vardır.
vsz

1
@gbjbaanb Fikir, sadece ZorkA veya ZorkB üzerinde özel olarak çalışabilecek bir müşteri sınıfına sahip olmak, hangisinin yakalanacağına müşteri sınıfı karar vermemek olduğuna inanıyorum.
Eugene Ryabtsev

39

Değişken olmayan küresellerin OOP'de kötü olmasının birçok nedeni vardır.

Bu şüpheli bir iddia. Eğer delil olarak kullanmak bağlantı atıfta devlet - değişken globaller. Onlar kesinlikle kötüdür. Salt okunur globaller sadece sabitlerdir. Sabitler nispeten aklı başında. Yani, pi'nin değerini tüm sınıflarınıza enjekte etmeyeceksiniz, değil mi?

Paylaşılması gereken nesnelerin sayısı veya boyutu işlev parametrelerinde verimli bir şekilde iletilemeyecek kadar büyükse, genellikle herkes global bir nesne yerine Bağımlılık Enjeksiyonu önerir.

Hayır.

İşlevlerinizin / sınıflarınızın çok fazla bağımlılığı varsa, insanlar durup tasarımınızın neden bu kadar kötü olduğuna bir göz atmanızı önerir. Tekne yükü bağımlılığı, sınıfınızın / fonksiyonunuzun muhtemelen çok fazla şey yaptığını ve / veya yeterli soyutluğunuzun olmadığını gösteren bir işarettir.

Tür, isim, renk, hız, konum vb. Gibi çok sayıda özelliğe ve duruma sahip bir dizi sanal araç vardır. Bir dizi kullanıcı bunları uzaktan kontrol edebilir ve çok sayıda olay (hem kullanıcı- başlatılmış ve otomatik) durumlarının veya özelliklerinin çoğunu değiştirebilir.

Bu bir tasarım kabusu. Hiçbir onaylama yöntemi, eşzamanlılık sınırlaması olmadan kullanılabilen / kötüye kullanılabilecek bir sürü öğeniz var - bu sadece her şeye bağlanıyor.

Bununla birlikte, eğer sınıfların çoğunluğu konteynere erişecekse, onu küreselden gerçekten farklı kılan nedir? Eğer çok fazla sınıfın kapsayıcıdaki verilere ihtiyacı varsa, "bağımlılık enjeksiyon yolu" sadece gizlenmiş bir küresel değil mi?

Evet, her yerde ortak verilere bağlanmak küresel olandan çok farklı değildir ve yine de kötüdür.

Sonuçta tüketicileri küresel değişkenin kendisinden ayırıyorsunuz. Bu sağlar bazı örneği oluşturulur nasıl esneklik. Ayrıca gelmez zorlamak yalnızca bir örneğini olan içine. Farklı tüketicilerin farklı tüketicilere aktarılmasını sağlayabilirsiniz. Ayrıca, ihtiyacınız olduğunda tüketici başına arayüzünüzün farklı uygulamalarını da oluşturabilirsiniz.

Ve kaçınılmaz olarak yazılımın değişen gereksinimleriyle birlikte gelen değişiklik nedeniyle, böyle temel bir esneklik seviyesi hayati önem taşımaktadır .


"değişmez" ile karışıklık için özür dilerim, değişebilir demek istedim ve bir şekilde hatamı tanımadı.
vsz

"Bu bir tasarım kabusu" - Biliyorum. Ancak gereksinimler, tüm bu olayların verilerdeki değişiklikleri yapabilmesi gerektiğinde , bunları bir soyutlama katmanı altında bölesem ve gizlesem bile olaylar verilere bağlanır. Bütün program hakkında bu veriler. Örneğin, bir program bir görüntü üzerinde birçok farklı işlem yapmak zorundaysa, sınıflarının tümü veya neredeyse tamamı bir şekilde görüntü verilerine bağlanır. Sadece "farklı bir şey yapan bir program yazalım" demek kabul edilemez.
vsz

2
Bazı veritabanlarına rutin olarak erişen birçok nesneye sahip uygulamalar tam olarak görülmemiş değildir.
Robert Harvey

@RobertHarvey - emin, ancak bağımlı oldukları arayüz "bir veritabanına erişebilir" veya " foos için bazı kalıcı veri deposu " mudur?
Telastyn

1
PHB'nin 500 özellik istediği ve Dilbert'in hayır dediği bir Dilbert'i hatırlatıyor. PHB 1 özellik ister ve Dilbert emin olur. Ertesi gün Dilbert, 1 özellik için 500 istek alır. Eğer bir şey ölçeklenmezse, nasıl giyindiğiniz önemli değil.
corsiKa

16

Göz önünde bulundurmanız gereken üç ana neden vardır.

  1. Okunabilirlik. Her kod birimi, enjekte edilmesi veya parametre olarak iletilmesi için ihtiyaç duyduğu her şeye sahipse, koda bakmak ve ne yaptığını anında görmek kolaydır. Bu, size endişeleri daha iyi ayırmanızı ve sizi düşünmeye zorlayan bir işlev yöresi verir ...
  2. Modülerlik. Ayrıştırıcı neden tüm araç listesi ve tüm özellikleri hakkında bilgi sahibi olmalıdır? Muhtemelen hayır. Belki bir araç kimliğinin var olup olmadığını veya X aracının Y özelliğine sahip olup olmadığını sorgulaması gerekir. Birdenbire çok daha mantıklı bir tasarıma sahip olursunuz, her kod parçası sadece onunla ilgili verileri işler. Bu da bizi ...
  3. Testedilebilirlik. Tipik bir birim testi için ortamınızı birçok farklı senaryo için ayarlamak istiyorsunuz ve enjeksiyon bunu uygulamayı çok kolaylaştırıyor. Yine, ayrıştırıcı örneğine dönersek, ayrıştırıcı için yazdığınız her test senaryosu için her zaman tam teşekküllü araçların tam listesini yapmak ister misiniz? Ya da sadece yukarıda belirtilen hizmet nesnesinin sahte numaralar oluşturarak değişen sayıda kimlik döndürür mü? Hangisini seçeceğimi biliyorum.

Özetlemek gerekirse: DI bir hedef değil, sadece bir hedefe ulaşmayı mümkün kılan bir araçtır. Yanlış kullanırsanız (örneğinizde yaptığınız gibi), hedefe yaklaşmayacaksınız, DI'nin kötü bir tasarımı iyi yapacak sihirli bir özelliği yoktur.


Bakım sorunlarına odaklanmış özlü bir cevap için +1. Daha fazla P: SE yanıtı bu şekilde olmalıdır.
dodgethesteamroller

1
Özetiniz çok iyi. Çok iyi. [Ayın tasarım deseni] veya [ayın kelime kelimesi] sorunlarınızı sihirli bir şekilde çözeceğini düşünüyorsanız, kötü zaman geçireceksiniz.
corsiKa

@corsiKa Uzun zamandır DI'ye (ya da daha kesin olmak gerekirse) karşı geldim çünkü amacını göremedim. Sadece somut bir kazanç ile korkunç bir çok güçlük gibi görünüyordu (bu XML yapılandırma günlerinde geri döndü). Keşke DI aslında o zamanlar size ne satın aldığını gösteren bazı belgeler bulmuş olsaydım. Ama ben yapmadım, bu yüzden kendim bulmalıydım. Uzun bir zaman aldı. :)
biziclop

3

Gerçekten test edilebilirlikle ilgili - normalde küresel bir nesne çok sürdürülebilir ve okunması / anlaşılması kolaydır (elbette orada olduğunu bildiğinizde), ancak kalıcı bir fikstürdür ve sınıflarınızı izole testlere bölmeye geldiğinizde, küresel nesne "benim hakkımda" diyerek takılıyor ve bu nedenle izole edilmiş testleriniz küresel nesnenin bunlara dahil edilmesini gerektiriyor. Çoğu test çerçevesinin bu senaryoyu kolayca desteklememesi yardımcı olmaz.

Evet, hala küresel. Buna sahip olmanın temel nedeni değişmedi, sadece ona erişilme şekli.

Başlatma konusunda, bu hala bir sorun - yine de yapılandırmayı testlerinizin çalışabilmesi için ayarlamanız gerekiyor, böylece orada hiçbir şey kazanmıyorsunuz. Büyük bir bağımlı nesneyi daha küçük nesnelere bölebilir ve uygun olanı onlara ihtiyaç duyan sınıflara geçirebilirsiniz, ancak muhtemelen herhangi bir faydadan daha ağır basmak için daha da karmaşık hale geliyorsunuz.

Tüm bunlardan bağımsız olarak, 'köpeği sallayan kuyruk' örneği, kodlama tabanının nasıl tasarlandığını test etme gereğidir (statik sınıflar, örneğin geçerli tarih gibi) ile benzer sorunlar ortaya çıkar.

Şahsen ben tüm global verilerimi global bir nesneye (ya da birkaç) yapıştırmayı tercih ediyorum ama sınıflarımın ihtiyaç duyduğu bitleri almak için erişimciler sağlıyorum. O zaman DI ek karmaşıklık olmadan test söz konusu olduğunda istediğim verileri döndürmek için alay edebilirim.


"normalde küresel bir nesne çok sürdürülebilir" - değişken ise değil. Benim tecrübelerime göre, çok değişken küresel bir devlete sahip olmak bir kabus olabilir (ancak katlanılabilir hale getirmenin yolları vardır).
sleske

3

Zaten sahip olduğunuz cevaplara ek olarak,

Tür, isim, renk, hız, konum vb. Gibi çok sayıda özelliğe ve duruma sahip bir dizi sanal araç vardır. Bir dizi kullanıcı bunları uzaktan kontrol edebilir ve çok sayıda olay (hem kullanıcı- başlatılmış ve otomatik) durumlarının veya özelliklerinin çoğunu değiştirebilir.

OOP programlamanın karakteristik özelliklerinden biri, yalnızca belirli bir nesne için gerçekten ihtiyacınız olan özellikleri korumaktır,

ŞİMDİ Nesneniz çok fazla özelliğe sahipse - nesnenizi alt nesnelere daha da bölmelisiniz.

Düzenle

Küresel nesnelerin neden kötü olduğunu daha önce belirtmiştim, buna bir örnek ekleyeceğim.

Windows formunun GUI kodunda birim testi yapmanız gerekir, global kullanıyorsanız, tüm düğmeyi oluşturmanız gerekir ve örneğin uygun bir test örneği oluşturmak için olaylar ...


Doğru, ancak örneğin ayrıştırıcı, herhangi bir şeyde değişiklik yapabilen herhangi bir komut alınabileceğinden her şeyi bilmelidir.
vsz

1
“Asıl sorunuza gelmeden önce” ... sorusunu cevaplayacak mısınız?
gbjbaanb

@gbjbaanb soru çok metin var, lütfen bana doğru okumak için biraz zaman tanıyın
Matematik
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.