Yapıcılar veya özellik belirleyiciler aracılığıyla bağımlılık enjeksiyonu?


151

Bir sınıfı yeniden düzenleyip ona yeni bir bağımlılık ekliyorum. Sınıf şu anda yapıcıdaki mevcut bağımlılıklarını almaktadır. Tutarlılık için parametreyi yapıcıya ekliyorum.
Tabii ki, birkaç alt sınıf artı ünite testleri için daha da fazlası var, bu yüzden şimdi tüm yapıcıları eşleştirmek için etrafta dolaşmak oyununu oynuyorum ve yaşlanıyorum.
Ayarlayıcılarla özellikleri kullanmanın bağımlılık almanın daha iyi bir yolu olduğunu düşündürüyor. Enjekte edilen bağımlılıkların bir sınıf örneği oluşturma arayüzünün bir parçası olması gerektiğini düşünmüyorum. Bir bağımlılık eklersiniz ve şimdi tüm kullanıcılarınız (alt sınıflar ve sizi doğrudan başlatan herkes) aniden bunu biliyor. Bu bir kapsülleme molası gibi geliyor.

Bu, burada mevcut kod ile desen gibi görünmüyor, bu yüzden genel mutabakatın ne olduğunu bulmak için arıyorum, artıları ve eksileri inşaat karşı eksilerini. Özellik ayarlayıcıları kullanmak daha mı iyi?

Yanıtlar:


126

Peki, bu :-) bağlıdır.

Sınıf işini bağımlılık olmadan yapamazsa, yapıcıya ekleyin. Sınıfın yeni bağımlılığa ihtiyacı var , bu yüzden değişikliğinizin bir şeyleri kırmasını istiyorsunuz. Ayrıca, tam olarak başlatılmamış bir sınıf oluşturmak ("iki aşamalı yapı") bir anti-modeldir (IMHO).

Sınıf bağımlılık olmadan çalışabiliyorsa, bir ayarlayıcı iyidir.


11
Ben birçok durumda Null Nesne desen kullanmak ve yapıcı üzerinde başvuru gerektiren sopa tercih tercih olduğunu düşünüyorum. Bu, tüm sıfır denetimini ve artan siklomatik karmaşıklığı önler.
Mark Lindell

3
@ Mark: İyi bir nokta. Ancak soru, mevcut bir sınıfa bağımlılık eklemekti. Daha sonra arg olmayan bir yapıcı tutmak geriye dönük uyumluluğa izin verir.
sleske

Bağımlılığın çalışması için ne zaman gerekli olduğu, ancak bu bağımlılığın varsayılan enjeksiyonu genellikle yeterli olacaktır. O zaman bu bağımlılık mülk veya yapıcı aşırı yükü tarafından "geçersiz kılınabilir" mi?
Patrick Szalapski

@Patrick: "Sınıf bağımlılığı olmadan işini yapamaz" ile, makul bir varsayılan (yani sınıf bir DB bağlantısı gerektirir) anlamına geliyordu. Sizin durumunuzda her ikisi de işe yarar. Yine de genellikle yapıcı yaklaşımını tercih ederim, çünkü karmaşıklığı azaltır (ya da setter iki kez çağrılırsa)?
sleske


21

Bir sınıfın kullanıcılarının belirli bir sınıfın bağımlılıkları hakkında bilgi sahibi olması gerekir. Örneğin, bir veritabanına bağlı bir sınıfım olsaydı ve bir kalıcılık katmanı bağımlılığı enjekte etmek için bir araç sağlamadıysam, kullanıcı veritabanına bir bağlantının kullanılabilir olması gerektiğini asla bilemezdi. Ancak, yapıcıyı değiştirirsem kullanıcılara kalıcılık katmanına bağımlılık olduğunu bildiririm.

Ayrıca, eski kurucunun her kullanımını değiştirmek zorunda kalmamak için, eski ve yeni kurucu arasında geçici bir köprü olarak kurucu zincirini uygulamanız yeterlidir.

public class ClassExample
{
    public ClassExample(IDependencyOne dependencyOne, IDependencyTwo dependencyTwo)
        : this (dependnecyOne, dependencyTwo, new DependnecyThreeConcreteImpl())
    { }

    public ClassExample(IDependencyOne dependencyOne, IDependencyTwo dependencyTwo, IDependencyThree dependencyThree)
    {
        // Set the properties here.
    }
}

Bağımlılık enjeksiyonunun noktalarından biri, sınıfın hangi bağımlılıklara sahip olduğunu ortaya çıkarmaktır. Sınıfın çok fazla bağımlılığı varsa, bazı yeniden düzenleme işlemlerinin gerçekleşme zamanı gelmiş olabilir: Sınıfın her yöntemi tüm bağımlılıkları kullanıyor mu? Değilse, bu sınıfın nereye ayrılabileceğini görmek için iyi bir başlangıç ​​noktasıdır.


Tabii ki yapıcı zincirleme sadece yeni parametre için makul bir varsayılan değer varsa çalışır. Ama aksi halde işleri
bozmaktan kaçınamazsınız

Genellikle bağımlılık enjeksiyonundan önce yöntemde kullandığınız her şeyi varsayılan parametre olarak kullanırsınız. İdeal olarak, bu, sınıfın davranışı değişmeyeceğinden, yeni yapıcı eklemesini temiz bir yeniden düzenleme haline getirecektir.
Doctor Blue

1
Veritabanı bağlantıları gibi kaynak yönetimi bağımlılıklarına dikkatinizi çekiyorum. Benim durumumda sorun, bağımlılık eklediğim sınıfın birkaç alt sınıfa sahip olduğunu düşünüyorum. Özelliğin kap tarafından ayarlanacağı bir IOC kap dünyasında, ayarlayıcıyı kullanmak en azından tüm alt sınıflar arasındaki yapıcı arabirimi çoğaltması üzerindeki baskıyı azaltacaktır.
Niall Connaughton

17

Tabii ki, yapıcıyı koymak, hepsini bir kerede doğrulayabileceğiniz anlamına gelir. Salt okunur alanlara bir şeyler atarsanız, inşaat süresinden itibaren nesnenizin bağımlılıkları hakkında bazı garantilere sahip olursunuz.

Yeni bağımlılıklar ekleyen gerçek bir acıdır, ancak en azından bu şekilde derleyici doğru olana kadar şikayet etmeye devam eder. Bu iyi bir şey, sanırım.


Artı bunun için. Buna ek olarak, bu dairesel bağımlılık tehlikesini son derece azaltır ...
gilgamash

11

Çok sayıda isteğe bağlı bağımlılığınız varsa (ki bu zaten bir kokudur), muhtemelen ayarlayıcı enjeksiyonu gitmenin yoludur. Yapıcı enjeksiyonu bağımlılıklarınızı daha iyi ortaya çıkarır.


11

Tercih edilen genel yaklaşım, mümkün olduğunca yapıcı enjeksiyonunu kullanmaktır.

Yapıcı enjeksiyonu, nesnenin düzgün çalışması için gereken bağımlılıkların tam olarak ne olduğunu belirtir - hiçbir şey, bir nesneyi yeni sürüme geçirmekten ve bir bağımlılık ayarlanmadığı için üzerine bir yöntem çağırırken çökmesinden daha sinir bozucu değildir. Bir kurucu tarafından döndürülen nesne çalışır durumda olmalıdır.

Sadece bir kurucuya sahip olmaya çalışın, tasarımı basit tutar ve belirsizliği önler (insanlar için değilse, DI kabı için).

Mark Seemann'ın ".NET'te Bağımlılık Enjeksiyonu" adlı kitabında yerel bir varsayılan olarak adlandırdığı şeye sahip olduğunuzda özellik enjeksiyonunu kullanabilirsiniz : bağımlılık isteğe bağlıdır, çünkü iyi çalışan bir uygulama sağlayabilirsiniz, ancak arayanın farklı bir uygulama belirtmesine izin vermek istersiniz. gerekli.

(Eski cevap aşağıdadır)


Enjeksiyon zorunlu ise yapıcı enjeksiyonunun daha iyi olduğunu düşünüyorum. Bu çok fazla kurucu eklerse, kurucular yerine fabrikaları kullanmayı düşünün.

Ayarlayıcı enjeksiyonu, enjeksiyon isteğe bağlıysa veya yarıyı değiştirmek istiyorsanız güzeldir. Genel olarak pasörlerden hoşlanmıyorum ama bu bir zevk meselesi.


Genellikle enjeksiyonun yarısını değiştirmenin kötü bir stil olduğunu iddia ediyorum (çünkü nesnenize gizli durum ekliyorsunuz). Ama tabii ki istisnasız kural yok ...
Sleske

Evet, bu yüzden ayarlayıcıyı çok sevmediğimi söyledim ... Yapıcı yaklaşımını seviyorum çünkü o zaman değiştirilemez.
Philippe

"Bu çok fazla kurucu eklerse, kurucu yerine fabrika kullanmayı düşünün." Temelde herhangi bir çalışma zamanı istisnalarını ertelersiniz ve hatta birtakım hatalar alabilir ve bir servis bulucu uygulaması ile sıkışıp kalabilir.
MeTitus

@Marco benim eski cevabım ve haklısın. Çok sayıda kurucu varsa, sınıfın çok fazla şey yaptığını iddia ederim :-) Veya soyut bir fabrikayı düşünün.
Philippe

7

Bu büyük ölçüde kişisel bir zevk meselesi. Şahsen setter enjeksiyonunu tercih etme eğilimindeyim, çünkü çalışma zamanında uygulamaları değiştirebilmeniz için size daha fazla esneklik verdiğine inanıyorum. Ayrıca, çok fazla argümana sahip kurucular benim görüşüme göre temiz değildir ve bir kurucuda sunulan argümanlar isteğe bağlı olmayan argümanlar ile sınırlandırılmalıdır.

Sınıflar arabirimi (API) görevini yerine getirmek için neye ihtiyaç duyduğu açık olduğu sürece, iyisinizdir.


lütfen neden indirdiğinizi belirtin?
nkr1pt

3
Evet, birçok argümanı olan kurucular kötüdür. Bu yüzden birçok yapıcı parametresi olan sınıfları yeniden düzenlersiniz :-).
sleske

10
@ nkr1pt: Çoğu kişi (ben dahil), enjeksiyon yapılmazsa çalışma zamanında başarısız olan bir sınıf oluşturmanıza izin verirse ayarlayıcı enjeksiyonunun kötü olduğunu kabul eder. Bu yüzden birisinin kişisel zevkiniz olduğuna dair beyanınıza itiraz ettiğine inanıyorum.
sleske

7

Ben şahsen büyük ölçüde sorunuzun ana hatları nedeniyle yapıcı enjekte bağımlılıkları üzerinde "desen" ayıkla ve geçersiz kılmayı tercih ederim . Özellikleri olarak ayarlayabilir virtualve sonra türetilebilir bir test edilebilir sınıftaki uygulamayı geçersiz kılabilirsiniz.


1
Desenin resmi adı "Şablon Yöntemi" olduğunu düşünüyorum.
davidjnelson

6

Yapıcı enjeksiyonunu tercih ediyorum çünkü bir sınıfın bağımlılık gereksinimlerini "zorunlu kılmaya" yardımcı oluyor. Oluşturucuda ise, bir tüketici , uygulamanın derlenmesini sağlamak için nesneleri ayarlamalıdır. Setter enjeksiyonu kullanırsanız, çalışma süresine kadar bir sorun yaşadıklarını bilmeyebilirler - ve nesneye bağlı olarak, çalışma süresinde geç olabilir.

Enjekte edilen nesnenin başlangıçta bir sürü iş gerektirebileceği zaman zaman ayarlayıcı enjeksiyonunu kullanıyorum.


6

Yapıcı enjeksiyonunu yapıyorum, çünkü bu en mantıklı görünüyor. Sınıfımın bu bağımlılıkların işini yapmasını gerektirdiğini söylemek gibi . İsteğe bağlı bir bağımlılık varsa özellikleri makul görünüyor.

Ayrıca kapsayıcı kullanılarak oluşturulan bir sunucu üzerinde ASP.NET görünümü gibi bir başvuru içermeyen şeyler ayarlamak için özellik enjeksiyon kullanın.

Enkapsülasyonu bozduğunu sanmıyorum. İç çalışmalar içsel kalmalı ve bağımlılıklar farklı bir endişeyle ilgilenmelidir.


Cevabınız için teşekkürler. Kesinlikle kurucu popüler cevap gibi görünüyor. Ancak bir şekilde kapsüllemeyi bozduğunu düşünüyorum. Bağımlılık öncesi enjeksiyon, sınıf işini yapmak için ihtiyaç duyduğu her türlü beton türünü ilan edecek ve örnekleyecektir. DI ile alt sınıflar (ve herhangi bir manuel anında kullanıcı) artık bir temel sınıfın hangi araçları kullandığını biliyor. Yeni bir bağımlılık eklersiniz ve şimdi bağımlılığı kendilerinin kullanmasına gerek olmasa bile örneği tüm alt sınıflardan zincirlemeniz gerekir.
Niall Connaughton

Sadece güzel uzun bir cevap yazdı ve bu sitede bir istisna nedeniyle kaybetti !! :( Özet olarak, temel sınıf genellikle mantığı yeniden kullanmak için kullanılır.Bu mantık alt sınıfa kolayca girebilir ... böylece alt sınıfı ve alt sınıfı düşünebilirsiniz. Bağımlılıklarınız olması, daha önce özel olarak
saklayacağınız bir

3

Dikkate değer bir seçenek, basit tek bağımlılıklardan karmaşık çoklu bağımlılıklar oluşturmaktır. Yani, bileşik bağımlılıklar için ekstra sınıflar tanımlayın. Bu, işleri biraz daha kolaylaştırır WRT yapıcı enjeksiyonu - çağrı başına daha az parametre - yine de her şey için zorunlu tedarik bağımlılıklarını korurken.

Elbette, bir çeşit mantıksal bağımlılık gruplaması olup olmadığı en mantıklıdır, bu nedenle bileşik keyfi bir toplamadan daha fazlasıdır ve tek bir bileşik bağımlılığı için birden fazla bağımlı olup olmadığı en mantıklıdır - ancak "model" parametre bloğu uzun zamandır buralardaydı ve gördüklerimin çoğu oldukça keyfi.

Kişisel olarak, ben daha çok bağımlılıkları, seçenekleri vb belirtmek için yöntemleri / özellik belirleyicileri kullanma hayranıyım. Çağrı adları neler olup bittiğini açıklamak yardımcı olur. Bununla birlikte, bu şekilde ayarlanabilen snippet'lere örnek vermek ve bağımlı sınıfın yeterli hata denetimi yaptığından emin olmak iyi bir fikirdir. Kurulum için sonlu durum modeli kullanmak isteyebilirsiniz.


3

Son zamanlarda bir sınıfta birden fazla bağımlılığım olduğu bir durumla karşılaştım , ancak her uygulamada bağımlılıklardan yalnızca biri değişecekti. Veri erişimi ve hata günlüğü bağımlılıkları yalnızca sınama amacıyla değiştirilebileceğinden, bu bağımlılıklar için isteğe bağlı parametreler ekledim ve yapıcı kodumdaki bu bağımlılıkların varsayılan uygulamalarını sağladım. Bu şekilde, sınıfın tüketicisi tarafından geçersiz kılınmadıkça sınıf varsayılan davranışını korur.

İsteğe bağlı parametrelerin kullanılması yalnızca .NET 4 gibi bunları destekleyen çerçevelerde gerçekleştirilebilir (VB.NET'in her zaman sahip olmasına rağmen, hem C # hem de VB.NET için). Tabii ki, benzer işlevselliği sadece sınıfınızın tüketicisi tarafından yeniden atanabilen bir özellik kullanarak gerçekleştirebilirsiniz, ancak yapıcı parametresine atanmış özel bir arabirim nesnesine sahip olarak sağlanan değişmezlik avantajından yararlanamazsınız.

Tüm bunlar söyleniyor, eğer her tüketici tarafından sağlanması gereken yeni bir bağımlılık getiriyorsanız, kurucunuzu ve tüketicilerinizin sınıfınızdaki tüm kodlarını yeniden düzenlemek zorunda kalacaksınız. Yukarıdaki önerilerim yalnızca geçerli kodunuzun tümü için varsayılan bir uygulama sağlayabilme lüksüne sahipseniz, ancak yine de gerekirse varsayılan uygulamayı geçersiz kılma olanağı sağladıysanız geçerlidir.


1

Bu eski bir gönderi, ancak gelecekte gerekiyorsa belki de bu herhangi bir işe yarar:

https://github.com/omegamit6zeichen/prinject

Benzer bir fikrim vardı ve bu çerçeveyi buldum. Muhtemelen tam olmaktan uzaktır, ancak mülk enjeksiyonuna odaklanan bir çerçeve fikri


0

Nasıl uygulamak istediğinize bağlıdır. Uygulamaya giren değerlerin sık sık değişmediğini hissettiğim her yerde yapıcı enjeksiyonunu tercih ederim. Örnek: Eğer compnay stragtegy oracle server ile giderse, veri kaynağı değerlerimi yapıcı enjeksiyonu yoluyla fasulye elde etme bağlantıları için yapılandıracağım. Başka, benim app bir ürün ve müşterinin herhangi bir db bağlanabilir şansı varsa, ben ayarlayıcı enjeksiyon yoluyla böyle db yapılandırma ve çoklu marka uygulama uygulamak olacaktır. Bir örnek aldım ancak yukarıda bahsettiğim senaryoları uygulamanın daha iyi yolları var.


1
Şirket içi kodlama yaparken bile, her zaman yeniden dağıtılabilecek kod geliştiren bağımsız bir yüklenici olduğumu düşünerek kod yazıyorum. Açık kaynak olacağını düşünüyorum. Bu şekilde, ben kod modüler ve takılabilir ve SOLID ilkeleri takip emin olun.
Fred

0

Oluşturucu enjeksiyonu bağımlılıkları açıkça ortaya koyar, kodlar daha okunaklı ve yapıcıda argümanlar kontrol edilirse işlenmemiş çalışma zamanı hatalarına daha az eğilimli olur, ancak gerçekten kişisel görüşe gelir ve DI'yi ne kadar çok kullanırsanız projeye bağlı olarak şu ya da bu şekilde ileri geri sallanma eğilimindedir. Kişisel olarak uzun bir argüman listesi ile yapıcılar gibi kod kokuları ile ilgili sorunları var ve bir nesne tüketicisi zaten nesneyi kullanmak için bağımlılıkları bilmek gerektiğini hissediyorum, bu özellik enjeksiyon kullanmak için bir durum yapar. Mülkiyet enjeksiyonunun örtülü doğasını sevmiyorum, ancak daha zarif buluyorum, daha temiz görünümlü kodlarla sonuçlanıyorum. Ancak öte yandan, yapıcı enjeksiyonu daha yüksek bir kapsülleme derecesi sunar,

Özel senaryonuza göre yapıcı veya mülke göre enjeksiyon seçin. Ve DI'yi sadece gerekli göründüğü için kullanmanız gerektiğini düşünmeyin ve kötü tasarım ve kod kokularını önleyecektir. Eğer çaba ve karmaşıklık faydadan daha ağır basarsa, bazen bir desen kullanmak için çaba göstermeye değmez. Basit tutun.

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.