Enjeksiyon bağımlılıkları ctorda mı yoksa yöntem başına mı yapılmalıdır?


17

Düşünmek:

public class CtorInjectionExample
{
    public CtorInjectionExample(ISomeRepository SomeRepositoryIn, IOtherRepository OtherRepositoryIn)
    {
        this._someRepository = SomeRepositoryIn;
        this._otherRepository = OtherRepositoryIn;
    }

    public void SomeMethod()
    {
        //use this._someRepository
    }

    public void OtherMethod()
    {
        //use this._otherRepository
    }
}

karşısında:

public class MethodInjectionExample
{
    public MethodInjectionExample()
    {
    }

    public void SomeMethod(ISomeRepository SomeRepositoryIn)
    {
        //use SomeRepositoryIn
    }

    public void OtherMethod(IOtherRepository OtherRepositoryIn)
    {
        //use OtherRepositoryIn
    }
}

Ctor enjeksiyonu uzantıyı zorlaştırırken (yeni bağımlılıklar eklendiğinde ctor'u çağıran herhangi bir kodun güncellenmesi gerekir) ve yöntem düzeyi enjeksiyonu sınıf düzeyinde bağımlılıktan daha fazla kapsüllenmiş gibi görünüyor ve bu yaklaşımlar için / bu yaklaşımlara karşı başka argümanlar bulamıyorum .

Enjeksiyon için kesin bir yaklaşım var mı?

(Not: Bu konuda bilgi aradım ve bu soruyu objektif hale getirmeye çalıştım.)


4
Sınıflarınız uyumluysa, birçok farklı yöntem aynı alanları kullanır. Bu aradığınız cevabı vermelidir ...
Oded

@ İyi nokta, uyum kararı büyük ölçüde etkiler. Bir sınıf, mesela, her birinin farklı bağımlılıklara sahip olduğu (görünüşte uyumlu değil, fakat mantıksal olarak benzer) ve diğerinin oldukça bağlı olduğu yardımcı yöntem koleksiyonları varsa, her biri en faydalı yaklaşımı kullanmalıdır. Ancak, bu bir çözümde tutarsızlığa neden olacaktır.
StuperUser

Yanıtlar:


3

Enjekte edilen nesne sınıfta birden fazla yöntem tarafından kullanılıyorsa ve her yönteme enjekte etmek mantıklı değilse, her yöntem çağrısı için bağımlılıkları çözmeniz gerekir, ancak bağımlılık yalnızca yapıcıya enjekte edilen bir yöntem iyi değildir, ancak asla kullanılamayacak kaynakları tahsis ettiğiniz için.


15

Doğru, ilk önce, "Yeni bağımlılıklar eklendiğinde ctor'u çağıran herhangi bir kodun güncellenmesi gerekecek"; Açık olmak gerekirse, bağımlılık enjeksiyonu yapıyorsanız ve bağımlılıkları olan bir nesne üzerinde new () yöntemini çağıran herhangi bir kodunuz varsa, bunu yanlış yapıyorsunuz demektir .

DI konteyneriniz ilgili tüm bağımlılıkları enjekte edebilmelidir, bu nedenle yapıcı imzasını değiştirme konusunda endişelenmenize gerek yoktur, böylece argüman gerçekten geçerli olmaz.

Yöntem başına ve sınıf başına enjeksiyon fikrine gelince, yöntem başına enjeksiyon ile ilgili iki önemli sorun vardır.

Bir sorun, sınıfınızın yöntemlerinin bağımlılıkları paylaşması gerektiğidir, çok sayıda bağımlılığa (muhtemelen 4-5'ten fazla) sahip bir sınıf görürseniz, bu sınıf en iyi adaydır. iki sınıfa yeniden düzenleme için.

Bir sonraki sorun, bağımlılıkları, yöntem başına "enjekte etmek" için bunları yöntem çağrısına geçirmeniz gerektiğidir. Bu, yöntem çağrısından önce bağımlılıkları çözmek zorunda kalacağınız anlamına gelir, bu nedenle muhtemelen böyle bir kod grubuyla sonuçlanırsınız:

var someDependency = ServiceLocator.Resolve<ISomeDependency>();
var something = classBeingInjected.DoStuff(someDependency);

Şimdi, bu yöntemi uygulamanızın 10 yerinde arayacağınızı varsayalım: bu snippet'lerden 10 tanesine sahip olacaksınız. Ardından, DoStuff () öğesine başka bir bağımlılık eklemeniz gerektiğini varsayalım: bu snippet'i 10 kez değiştirmeniz gerekecek (veya bir yönteme sarılacaksınız, bu durumda DI davranışını manuel olarak çoğaltıyorsunuz, zaman).

Yani, temelde orada yaptığınız şey, DI kullanan sınıflarınızı kendi DI konteynırlarından haberdar etmenizi sağlar, ki bu temel olarak kötü bir fikirdir , çünkü çok hızlı bir şekilde bakımı zor olan tıknaz bir tasarıma yol açar.

Bunu yapıcı enjeksiyonuyla karşılaştırın; Yapıcı enjeksiyonunda belirli bir DI konteynerine bağlı değilsiniz ve sınıflarınızın bağımlılıklarını yerine getirmekten asla doğrudan sorumlu değilsiniz, bu nedenle bakım oldukça baş ağrısından kaynaklanmıyor.

Bir grup ilgisiz yardımcı yöntem içeren bir sınıfa IoC uygulamaya çalıştığınız gibi geliyor, oysa yardımcı sınıfı kullanıma dayalı olarak bir dizi hizmet sınıfına ayırmaktan, daha sonra çağırır. Bu hala harika bir yaklaşım değil (sadece onlara iletilen argümanlarla uğraşmaktan daha karmaşık bir şey yapan yöntemlerle sınıflandırılmış yardımcı genellikle kötü yazılmış hizmet sınıflarıdır), ancak en azından tasarımınızı biraz daha temiz tutacaktır. .

(Not: Daha önce önerdiğiniz yaklaşımı yaptım ve o zamandan beri tekrarlamadığım çok kötü bir fikirdi. Gerçekten ayrılması gerekmeyen sınıfları ayırmaya çalıştığım ortaya çıktı ve sonuçlandı Her yöntem çağrısının diğer arabirimlerin neredeyse sabit bir seçimini gerektirdiği bir dizi arabirim ile. Bakım yapmak bir kabustu.)


1
"if you're doing dependency injection and you have any code calling new() on an object with dependencies, you're doing it wrong."Bir sınıfta bir yöntemi test ediyorsanız, bunu başlatmanız gerekir mi yoksa Test Altında Kodunuzu başlatmak için DI'yi mi kullanıyorsunuz?
StuperUser

@StuperUser Birim testi biraz farklı bir senaryodur, yorumum yalnızca üretim kodu için geçerlidir. Her biri önceden yapılandırılmış bir davranışa ve bir dizi varsayım ve iddiaya sahip bir alaycı çerçeve aracılığıyla testleriniz için sahte (veya saplama) bağımlılıklar yapacağınız için bunları elle enjekte etmeniz gerekir.
Ed James

1
Bu dediğimde bahsettiğim "any code calling the ctor will need updating"kod, üretim kodum ktorları çağırmıyor. Bu nedenlerden dolayı.
StuperUser

2
@StuperUser Fuarı yeterince, ancak hala gerekli bağımlılıklarla yöntemleri çağırıyorsunuz, yani cevabımın geri kalanı hala duruyor! Dürüst olmak gerekirse, birim testi perspektifinden yapıcı enjeksiyonu ve tüm bağımlılıkları zorlama konusunda anlaşıyorum. Bu, aslında herhangi bir kod yazmak zorunda kalmadan temelde örtülü Assert.WasNotCalled çağrıları başka bir dizi olduğunu bulmak için bir test için gereken beklediğiniz bağımlılıkları enjekte izin verir gibi özellik enjeksiyon kullanma eğilimindedir! : D
Ed James

3

Çeşitli kontrol türleri ters çevrilir ve sorunuz sadece iki tane önerir (bağımlılığı enjekte etmek için fabrikayı da kullanabilirsiniz).

Cevap: ihtiyaca bağlıdır. Bazen bir tür ve başka bir tür kullanmak daha iyidir.

Ben şahsen yapıcı enjektörlerini seviyorum, çünkü kurucu nesneyi tamamen başlatmak için orada. Daha sonra bir ayarlayıcıyı çağırmak, nesneyi tam olarak inşa edilmemiş hale getirir.


3

En azından benim için en esnek özellik olanı kaçırdın. Castle / Windsor kullanıyorum ve tek yapmam gereken, sınıfıma yeni bir oto özellik eklemek ve enjekte edilen nesneyi sorunsuz ve herhangi bir arayüz kırmadan almak.


Web uygulamalarına alışkınım ve bu sınıflar, bağımlılıkları zaten Özellik Enjeksiyonuna benzer bir şekilde çözülmüş olan UI tarafından çağrılan Etki Alanı katmanındaysa. Enjekte edilen nesneleri aktarabileceğim sınıflarla nasıl başa çıkacağımı merak ediyorum.
StuperUser

Ayrıca özellik enjeksiyonunu da seviyorum, bu yüzden hala normal yapıcı parametrelerini hala bir DI konteyneri ile kullanabilirsiniz, böylece çözülmüş ve çözülmemiş bağımlılıklar arasında otomatik olarak ayrım yapın. Bununla birlikte, yapıcı ve özellik enjeksiyonu bu senaryo için işlevsel olarak aynı olduğundan, bundan bahsetmemeyi seçtim;)
Ed James

Özellik enjeksiyonu her nesneyi değişebilir olmaya zorlar ve geçersiz bir durumda örnekler oluşturur. Bu neden tercih edilir?
Chris Pitman

2
@Chris Aslında, normal koşullar altında, yapıcı ve özellik enjeksiyonu hemen hemen aynı şekilde hareket eder, cisim çözünürlük sırasında tamamen enjekte edilir. "Geçersiz" olarak kabul edilebilecek tek zaman, uygulamayı kaplamak için DI kapsayıcısını kullanmayacağınız zaman birim testi sırasındadır. Bunun aslında neden faydalı olduğuna dair benim cevabım hakkındaki yorumuma bakın. Değişebilirliğin bir sorun olabileceğini takdir ediyorum, ancak gerçekçi olarak yalnızca çözülmüş sınıflara arabirimleri aracılığıyla başvuracaksınız, bu da düzenleme bağımlılıklarını ortaya çıkarmayacak.
Ed James
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.