Neden bağımlılık enjeksiyonu kullanılır?


536

Bağımlılık enjeksiyonlarını (DI) anlamaya çalışıyorum ve bir kez daha başarısız oldum. Sadece aptalca görünüyor. Kodum asla bir karmaşa değildir; Ben neredeyse sanal fonksiyonları ve arayüzleri yazmak (mavi ay bir kez yapmak rağmen) ve tüm benim konfigürasyon sihirli json.net (bazen XML serileştirici kullanarak) kullanarak bir sınıfa serileştirilir.

Hangi problemi çözdüğünü tam olarak anlamıyorum. Söylemenin bir yolu gibi görünüyor: "merhaba. Bu işleve girdiğinizde, bu tür bir nesne döndürün ve bu parametreleri / verileri kullanın."
Ama ... bunu neden hiç kullanayım? Not Hiç kullanmam objectda gerekmedi , ama bunun ne için olduğunu anlıyorum.

Bir kişinin DI kullanacağı bir web sitesi veya masaüstü uygulaması oluştururken bazı gerçek durumlar nelerdir? Birisinin neden bir oyunda arabirimleri / sanal işlevleri kullanmak isteyebileceğine ilişkin vakaları kolayca bulabilirim, ancak bunu oyun dışı kodda kullanmak son derece nadirdir (tek bir örneği hatırlayamayacağım kadar nadirdir).


3
Bu ayrıca yararlı bilgiler olabilir: martinfowler.com/articles/injection.html
ta.speot.is




Yanıtlar:


840

İlk olarak, bu cevap için yaptığım varsayımı açıklamak istiyorum. Her zaman doğru değil, ancak sıklıkla:

Arayüzler sıfatlardır; sınıflar isimlerdir.

(Aslında, isimler de olan arayüzler var, ama burada genelleştirmek istiyorum.)

Yani, örneğin bir arayüz IDisposable, IEnumerableveya gibi bir şey olabilir IPrintable. Sınıf, bu arabirimlerin bir veya daha fazlasının gerçek bir uygulamasıdır: Listveya Mapher ikisi de IEnumerable.

Konuya ulaşmak için: Genellikle sınıflarınız birbirine bağlıdır. Örneğin Database, veritabanınıza erişen bir sınıfa (hah, sürpriz! ;-)) sahip olabilirsiniz, ancak bu sınıfın veritabanına erişme hakkında günlük kaydı yapmasını da istersiniz. Başka bir sınıfınız olduğunu varsayalım Logger, o zaman Databasebağımlılığınız var Logger.

Çok uzak çok iyi.

Bu bağımlılığı Databasesınıfınızda aşağıdaki satırla modelleyebilirsiniz :

var logger = new Logger();

ve her şey yolunda. Bir sürü kaydediciye ihtiyacınız olduğunu fark ettiğiniz güne kadar iyi olur: Bazen konsola, bazen dosya sistemine, bazen TCP / IP ve uzak bir günlük sunucusu kullanarak oturum açmak istersiniz ...

Ve do kursu DEĞİL tüm kodunu (bu arada bunun gazillions var) değiştirip tüm hatları değiştirmek istiyor

var logger = new Logger();

tarafından:

var logger = new TcpLogger();

İlk olarak, bu eğlenceli değil. İkincisi, bu hataya yatkındır. Üçüncüsü, bu eğitimli bir maymun için aptal, tekrarlayan bir çalışma. Ee ne yapıyorsun?

Açıkçası ICanLog, çeşitli kaydediciler tarafından uygulanan bir arayüz (veya benzeri) tanıtmak oldukça iyi bir fikirdir . Kodunuzdaki 1. adım, şunları yapmanızdır:

ICanLog logger = new Logger();

Artık tür çıkarımı artık türü değiştirmiyor, her zaman karşı karşıya gelecek tek bir arabirim var. Bir sonraki adım, new Logger()tekrar tekrar sahip olmak istemediğinizdir . Böylece, tek bir merkezi fabrika sınıfına yeni örnekler oluşturma güvenilirliğini koyarsınız ve aşağıdaki gibi bir kod alırsınız:

ICanLog logger = LoggerFactory.Create();

Fabrikanın kendisi ne tür bir kaydedici oluşturacağına karar verir. Kodunuz artık umursamıyor ve kullanılan günlükçünün türünü değiştirmek istiyorsanız, kodu bir kez değiştirirsiniz : Fabrika içinde.

Şimdi, elbette, bu fabrikayı genelleştirebilir ve herhangi bir tür için çalışmasını sağlayabilirsiniz:

ICanLog logger = TypeFactory.Create<ICanLog>();

Bu TypeFactory'nin bir yerinde, belirli bir arabirim türü istendiğinde somutlaştırılacak gerçek sınıfın yapılandırma verilerine ihtiyacı vardır, bu nedenle bir eşlemeye ihtiyacınız vardır. Tabii ki bu eşlemeyi kodunuzun içinde yapabilirsiniz, ancak daha sonra bir tür değişikliği yeniden derleme anlamına gelir. Ancak bu eşlemeyi bir XML dosyasının içine de yerleştirebilirsiniz, örn. Bu, derleme süresinden (!) Sonra bile gerçekte kullanılan sınıfı değiştirmenizi sağlar, yani dinamik olarak yeniden derlemeden demektir!

Bunun için yararlı bir örnek vermek gerekirse: Normal olarak oturum açmayan bir yazılım düşünün, ancak müşteriniz bir sorun olduğu için yardım istediğinde, ona gönderdiğiniz tek şey güncellenmiş bir XML yapılandırma dosyasıdır ve şimdi günlüğe kaydetme etkinleştirilir ve desteğiniz, müşterinize yardımcı olmak için günlük dosyalarını kullanabilir.

Ve şimdi, isimleri biraz değiştirdiğinizde, Inversion of Control için iki modelden biri olan basit bir Servis Bulucu uygulamasıyla sonuçlanırsınız (çünkü kimin hangi sınıfın somutlaştırılacağına karar vermesini tersine çevirdiğinizden).

Tüm bunlar, kodunuzdaki bağımlılıkları azaltır, ancak şimdi tüm kodlarınız merkezi, tek servis bulucuya bağımlıdır.

Bağımlılık enjeksiyonu şimdi bu satırdaki bir sonraki adımdır: Sadece servis bulucuya olan bu tek bağımlılıktan kurtulun: Servis bulucudan belirli bir arayüz için bir uygulama istemesini isteyen çeşitli sınıflar yerine, bir kez daha kimin neyi örneklediğini kontrol edersiniz .

Bağımlılık enjeksiyonu ile, Databasesınıfınızda artık bir tür parametre gerektiren bir kurucu var ICanLog:

public Database(ICanLog logger) { ... }

Artık veritabanınızın her zaman kullanmak için bir kaydedicisi vardır, ancak artık bu kaydedicinin nereden geldiğini bilmemektedir.

Ve burada bir DI çerçevesi devreye girer: Eşlemelerinizi bir kez daha yapılandırırsınız ve ardından DI çerçevenizden uygulamanızı sizin için somutlaştırmasını istersiniz. Gibi Applicationsınıf bir gerektiren ICanPersistDatauygulama, bir örneği Databaseenjekte edilir - ama bunun için öncelikle için yapılandırılmış kaydedici tür bir örneğini oluşturmalısınız ICanLog. Ve bunun gibi ...

Yani, uzun bir hikaye kısaltmak için: Bağımlılık enjeksiyonu, kodunuzdaki bağımlılıkları nasıl kaldırmanın iki yolundan biridir. Derleme süresinden sonra yapılandırma değişiklikleri için çok yararlıdır ve birim testi için harika bir şeydir (saplamaları ve / veya alayları enjekte etmeyi çok kolaylaştırdığı için).

Uygulamada, bir servis bulucu olmadan yapamayacağınız şeyler vardır (örneğin, belirli bir arayüzde kaç örneğe ihtiyacınız olduğunu önceden bilmiyorsanız: Bir DI çerçevesi her parametre için yalnızca bir örnek enjekte eder, ancak tabii ki bir döngü içindeki bir servis bulucu), dolayısıyla her DI çerçevesi de bir servis bulucu sağlar.

Ama temelde, hepsi bu.

Not: Burada tarif ettiğim, yapıcı enjeksiyonu adı verilen bir tekniktir , ayrıca yapıcı parametreleri olmayan özellik enjeksiyonu da vardır , ancak özellikler bağımlılıkları tanımlamak ve çözmek için kullanılmaktadır. Özellik enjeksiyonunu isteğe bağlı bir bağımlılık ve yapıcı enjeksiyonunu zorunlu bağımlılıklar olarak düşünün. Ancak bu konudaki tartışma bu sorunun kapsamı dışındadır.


7
Elbette bunu da bu şekilde yapabilirsiniz, ancak daha sonra bu mantığı, uygulamanın değiştirilebilirliği için destek sağlayacak her sınıfta uygulamanız gerekir. Bu, çoğaltılan, gereksiz kod anlamına gelir ve bu da şimdi ihtiyacınız olduğuna karar verdiğinizde mevcut bir sınıfa dokunmanız ve kısmen yeniden yazmanız gerektiği anlamına gelir. DI bunu herhangi bir keyfi sınıfta kullanmanıza izin verir, bunları özel bir şekilde yazmanız gerekmez (bağımlılıkları yapıcıda parametre olarak tanımlamak dışında).
Golo Roden

137
İşte DI hakkında hiçbir zaman elde edemediğim şey: mimariyi çok daha karmaşık hale getiriyor . Ve yine de, gördüğüm gibi, kullanım oldukça sınırlıdır. Örnekler her zaman aynıdır: değiştirilebilir kaydediciler, değiştirilebilir model / veri erişimi. Bazen değiştirilebilir görünüm. Ama bu kadar. Bu birkaç durum gerçekten çok daha karmaşık bir yazılım mimarisini haklı çıkarıyor mu? - Tam açıklama: DI'yi zaten büyük bir etki için kullandım, ancak bu genellemediğim çok özel bir eklenti mimarisi içindi.
Konrad Rudolph

17
@GoloRoden, neden ILogger yerine ICanLog arayüzünü çağırıyorsunuz? Bunu sık sık yapan başka bir programcıyla çalıştım ve sözleşmeyi asla anlayamadım mı? Benim için bu IEnumerable ICanEnumerate demek?
DermFrench

28
ICanLog olarak adlandırdım, çünkü hiçbir şey ifade etmeyen kelimelerle (isimler) çok sık çalışıyoruz. Örneğin, Broker nedir? Bir yönetici? Havuz bile benzersiz bir şekilde tanımlanmamıştır. Ve tüm bunları isimler olarak kullanmak, OO dillerinin tipik bir hastalığıdır (bkz. Steve-yegge.blogspot.de/2006/03/… ). Ne ifade etmek istiyorum benim için günlüğü yapabilir bir bileşen var - öyleyse neden bu şekilde adlandırmıyorsunuz? Tabii ki, bu aynı zamanda I ile ilk kişi olarak oynuyor, dolayısıyla ICanLog (ForYou).
Golo Roden

18
@David Unit testi gayet iyi çalışıyor - sonuçta, bir birim diğer şeylerden bağımsızdır (aksi takdirde bir birim değildir). Ne gelmez DI kapları olmadan işe sahte testidir. Yeterince adil, alay etmenin yararının, her durumda DI kapları eklemenin ek karmaşıklığına ağır bastığından emin değilim. Titiz birim testi yapıyorum. Nadiren alay ediyorum.
Konrad Rudolph

499

İnsanların bağımlılık enjeksiyonu ve bir bağımlılık enjeksiyon çerçevesi (veya genellikle bir kapsayıcı olarak adlandırılır) arasındaki fark hakkında birçok kez kafası karışıyor .

Bağımlılık enjeksiyonu çok basit bir kavramdır. Bu kod yerine:

public class A {
  private B b;

  public A() {
    this.b = new B(); // A *depends on* B
  }

  public void DoSomeStuff() {
    // Do something with B here
  }
}

public static void Main(string[] args) {
  A a = new A();
  a.DoSomeStuff();
}

şöyle kod yazıyorsunuz:

public class A {
  private B b;

  public A(B b) { // A now takes its dependencies as arguments
    this.b = b; // look ma, no "new"!
  }

  public void DoSomeStuff() {
    // Do something with B here
  }
}

public static void Main(string[] args) {
  B b = new B(); // B is constructed here instead
  A a = new A(b);
  a.DoSomeStuff();
}

Ve bu kadar. Ciddi anlamda. Bu size bir ton avantaj sağlar. İki önemli olan, Main()işlevselliği programınıza yaymak yerine merkezi bir yerden ( işlev) kontrol etme becerisi ve her sınıfı ayrı ayrı daha kolay bir şekilde test etme yeteneğidir (çünkü bunun yerine alayları veya diğer sahte nesneleri yapıcısına geçirebilirsiniz. gerçek bir değer).

Dezavantajı, elbette, şimdi programınız tarafından kullanılan tüm sınıfları bilen bir mega fonksiyona sahip olmanızdır. DI çerçeveleri bu konuda yardımcı olabilir. Ancak bu yaklaşımın neden değerli olduğunu anlamada sorun yaşıyorsanız, önce manuel bağımlılık enjeksiyonuyla başlamanızı öneririm, böylece orada çeşitli çerçevelerin sizin için neler yapabileceğini daha iyi anlayabilirsiniz.


7
Neden ilk kodu değil, ikinci kodu tercih ederim? İlki yalnızca yeni anahtar kelimeye sahipse, bu nasıl yardımcı olur?
user962206

17
@ user962206 A'yı B
.

66
@ user962206, ayrıca, B yapıcısında bazı parametrelere ihtiyaç duyarsa ne olacağını düşünün: onu somutlaştırmak için A bu parametreler hakkında bilmek zorunda kalacaktı, A ile tamamen ilgisiz olabilecek bir şey (sadece B'ye bağlı olmak istiyor , B'nin neye bağlı olduğuna göre değil). Zaten oluşturulmuş bir B'yi (veya bu konu için herhangi bir alt sınıfı veya B'yi) A'nın yapıcısına geçirmek bunu çözer ve A'yı sadece B'ye bağımlı hale getirir :)
epidemiyen

17
@ acidzombie24: Birçok tasarım deseni gibi, kod tabanınız basit bir yaklaşımın sorun haline gelmesi için yeterince büyük olmadıkça DI gerçekten kullanışlı değildir. Bağırsak hissim, uygulamanızın yaklaşık 20.000'den fazla kod satırı ve / veya diğer kütüphanelere veya çerçevelere 20'den fazla bağımlılığa sahip olmasından önce DI'nin aslında bir gelişme olmayacağıdır. Uygulamanız bundan daha küçükse, yine de bir DI tarzında programlamayı tercih edebilirsiniz, ancak fark neredeyse dramatik olmayacaktır.
Daniel Pryden

2
@DanielPryden Kod boyutunun kodunuzun ne kadar dinamik olduğu kadar önemli olduğunu düşünmüyorum. düzenli olarak aynı arayüze uyan yeni modüller ekliyorsanız, bağımlı kodu sık sık değiştirmeniz gerekmez.
FistOfFury

35

Diğer cevapların belirttiği gibi, bağımlılık enjeksiyonu bağımlılıklarınızı onu kullanan sınıfın dışında yaratmanın bir yoludur. Onları dışarıdan enjekte edersiniz ve yaratılışları hakkında sınıfınızın içinden kontrol edersiniz. Bu nedenle bağımlılık enjeksiyonu, Kontrolün İnversiyonu (IoC) prensibinin gerçekleştirilmesidir.

IoC, DI'nin kalıp olduğu prensiptir. "Birden fazla kayıt cihazına ihtiyaç duymanızın" sebebi, deneyimlerim kadarıyla asla karşılanmaz, ancak asıl neden, bir şeyi test ettiğinizde gerçekten ihtiyacınız olmasıdır. Bir örnek:

Özelliğim:

Bir teklife baktığımda, teklifime otomatik olarak baktığımı işaretlemek istiyorum, böylece yapmayı unutmam.

Bunu şu şekilde test edebilirsiniz:

[Test]
public void ShouldUpdateTimeStamp
{
    // Arrange
    var formdata = { . . . }

    // System under Test
    var weasel = new OfferWeasel();

    // Act
    var offer = weasel.Create(formdata)

    // Assert
    offer.LastUpdated.Should().Be(new DateTime(2013,01,13,13,01,0,0));
}

Yani bir yerde OfferWeasel, size böyle bir teklif Nesnesi oluşturur:

public class OfferWeasel
{
    public Offer Create(Formdata formdata)
    {
        var offer = new Offer();
        offer.LastUpdated = DateTime.Now;
        return offer;
    }
}

Buradaki sorun, bu testin büyük olasılıkla her zaman başarısız olacağıdır, çünkü ayarlanan tarih iddia edilen tarihten farklı olacaktır, sadece DateTime.Nowtest kodunu girseniz bile birkaç milisaniye kadar kapalı olabilir ve bu nedenle her zaman başarısız olur. Şimdi daha iyi bir çözüm, bunun için hangi zamanın ayarlanacağını kontrol etmenizi sağlayan bir arayüz oluşturmak olacaktır:

public interface IGotTheTime
{
    DateTime Now {get;}
}

public class CannedTime : IGotTheTime
{
    public DateTime Now {get; set;}
}

public class ActualTime : IGotTheTime
{
    public DateTime Now {get { return DateTime.Now; }}
}

public class OfferWeasel
{
    private readonly IGotTheTime _time;

    public OfferWeasel(IGotTheTime time)
    {
        _time = time;
    }

    public Offer Create(Formdata formdata)
    {
        var offer = new Offer();
        offer.LastUpdated = _time.Now;
        return offer;
    }
}

Arayüz soyutlamadır. Biri GERÇEK şey, diğeri ise ihtiyaç duyulduğu zaman sahte olmanıza izin verir. Test daha sonra şu şekilde değiştirilebilir:

[Test]
public void ShouldUpdateTimeStamp
{
    // Arrange
    var date = new DateTime(2013, 01, 13, 13, 01, 0, 0);
    var formdata = { . . . }

    var time = new CannedTime { Now = date };

    // System under test
    var weasel= new OfferWeasel(time);

    // Act
    var offer = weasel.Create(formdata)

    // Assert
    offer.LastUpdated.Should().Be(date);
}

Bu şekilde, bir bağımlılık (şimdiki zamanı elde ederek) enjekte ederek "kontrolün ters çevrilmesi" ilkesini uyguladınız. Bunu yapmanın ana nedeni, daha kolay izole ünite testi yapmaktır, bunu yapmanın başka yolları da vardır. Örneğin, burada bir arabirim ve bir sınıf gereksizdir çünkü C # işlevlerinde değişkenler olarak iletilebilir, bu nedenle bir arabirim yerine Func<DateTime>bunu elde etmek için a kullanabilirsiniz . Veya, dinamik bir yaklaşım kullanırsanız, sadece eşdeğer yöntemi olan ( ördek yazma ) herhangi bir nesneyi geçirirsiniz ve hiç bir arayüze ihtiyacınız yoktur.

Birden fazla kayıt cihazına ihtiyacınız olmayacak. Bununla birlikte, bağımlılık enjeksiyonu Java veya C # gibi statik olarak yazılan kodlar için gereklidir.

Ve ... Ayrıca, bir nesnenin tüm bağımlılıkları mevcutsa, çalışma zamanında amacını uygun bir şekilde yerine getirebileceği de unutulmamalıdır, bu nedenle özellik enjeksiyonunun ayarlanmasında çok fazla kullanım yoktur. Bence, tüm bağımlılıklar kurucu çağrıldığında tatmin edilmelidir, bu yüzden kurucu enjeksiyon devam edecek şeydir.

Umarım yardımcı olmuştur.


4
Bu gerçekten korkunç bir çözüm gibi görünüyor. Kesinlikle daha Daniel Pryden cevap önermek gibi kod yazmak istiyorum ama bu belirli birim testi için ben sadece DateTime.Now önce ve sonra işlevi yapmak ve zaman arasında olup olmadığını kontrol? Daha fazla arayüz / daha fazla kod satırı eklemek benim için kötü bir fikir gibi görünüyor.

3
Genel A (B) örneklerinden hoşlanmıyorum ve bir kaydedicinin 100 uygulamaya sahip olması gerektiğini hiç hissetmedim. Bu, son zamanlarda karşılaştığım bir örnektir ve bunu çözmek için 5 yoldan biridir, burada PostSharp'ı kullanarak. Klasik sınıf tabanlı bir ctor enjeksiyon yaklaşımını göstermektedir. DI için iyi bir kullanımla karşılaştığınız yerlere daha iyi bir gerçek dünya örneği verebilir misiniz?
Processor

2
DI için hiç iyi bir kullanım görmedim. Bu yüzden soruyu yazdım.

2
Yararlı bulamadım. Kodumu test etmek her zaman kolaydır. DI, kötü kod içeren büyük kod tabanları için iyi görünüyor.

1
Küçük fonksiyonel programlarda bile, f (x), g (f) olduğunda, zaten bağımlılık enjeksiyonu kullandığınızı unutmayın, böylece JS'deki her devam, bir bağımlılık enjeksiyonu olarak sayılır. Benim tahminim zaten kullanıyorsunuz;)
cessor

15

Klasik cevabın, çalışma sırasında hangi uygulamanın kullanılacağı hakkında hiçbir bilgisi olmayan daha ayrıştırılmış bir uygulama oluşturmak olduğunu düşünüyorum.

Örneğin, dünya çapında birçok ödeme sağlayıcısıyla çalışan merkezi bir ödeme sağlayıcısıyız. Ancak, bir istek yapıldığında, hangi ödeme işlemcisini arayacağımı bilmiyorum. Bir sınıfı bir ton anahtar kutusu ile programlayabilirim, örneğin:

class PaymentProcessor{

    private String type;

    public PaymentProcessor(String type){
        this.type = type;
    }

    public void authorize(){
        if (type.equals(Consts.PAYPAL)){
            // Do this;
        }
        else if(type.equals(Consts.OTHER_PROCESSOR)){
            // Do that;
        }
    }
}

Şimdi, tüm bu kodu tek bir sınıfta tutmanız gerekeceğini düşünün, çünkü düzgün bir şekilde ayrıştırılmadı, destekleyeceğiniz her yeni işlemci için yeni bir // // her yöntem, bu sadece daha karmaşık hale gelir, ancak, Bağımlılık Enjeksiyonu (veya Kontrolün İnversiyonu) kullanılarak - bazen de adlandırılır, yani programın çalışmasını kontrol eden her kimin komplikasyon değil, yalnızca çalışma zamanında bilindiği), bir şey elde edebilirsiniz çok temiz ve bakımı kolay.

class PaypalProcessor implements PaymentProcessor{

    public void authorize(){
        // Do PayPal authorization
    }
}

class OtherProcessor implements PaymentProcessor{

    public void authorize(){
        // Do other processor authorization
    }
}

class PaymentFactory{

    public static PaymentProcessor create(String type){

        switch(type){
            case Consts.PAYPAL;
                return new PaypalProcessor();

            case Consts.OTHER_PROCESSOR;
                return new OtherProcessor();
        }
    }
}

interface PaymentProcessor{
    void authorize();
}

** Kod derlenmeyecek, biliyorum :)


+1 çünkü sanal yöntemleri / arayüzleri kullandığınız yere ihtiyacınız olduğunu söylüyorsunuz. Ama bu hala nadir. Hala new ThatProcessor()bir çerçeve olarak kullanmak istiyorum

@ItaiS Sınıf fabrika tasarım deseni ile sayısız anahtardan kaçınabilirsiniz. Yansıma kullanın System.Reflection.Assembly.GetExecutingAssembly (). CreateInstance ()
domenicr

@domenicr ofcourse! ama basitleştirilmiş bir örnekle anlatmak istedim
Itai Sagi

Fabrika sınıfının ihtiyacı dışında yukarıdaki açıklamaya katılıyorum. Fabrika sınıfını uyguladığımız an sadece kaba bir seçim. Yukarıdakilerin en iyi açıklaması Bruce Erkel'in polimorfizm ve sanal fonksiyon bölümünde buldum. Gerçek DI seçimden bağımsız olmalı ve nesne türüne çalışma sırasında arayüz üzerinden otomatik olarak karar verilmelidir. Gerçek polimorfik davranış da budur.
Arvind Krmar

Örneğin (c ++ 'a göre), temel sınıf referansını alan ve türetme sınıfının davranışını seçim yapmadan uygulayan ortak bir arayüze sahibiz. void tune (Instrument & i) {i.play (middleC); int main () {Rüzgar flütü; ayar (yiv); } Enstrüman temel sınıftır, rüzgar ondan türetilir. C ++ 'a göre, sanal fonksiyon bunu ortak arabirim aracılığıyla türetilmiş sınıfın davranışını uygulamayı mümkün kılar.
Arvind Krmar

6

DI'yi kullanmanın temel nedeni, bilginin bulunduğu yerde uygulamanın bilgisinin sorumluluğunu koymak istemenizdir. DI fikri, arayüze göre kapsülleme ve tasarım ile çok fazla satır içi. Ön uç arka uçtan bazı veriler isterse, arka ucun bu soruyu nasıl çözdüğü önemsizdir. Bu istekte bulunan kişiye bağlıdır.

Bu uzun bir süre OOP'de zaten yaygındır. Çoğu zaman kod parçaları oluşturmak gibi:

I_Dosomething x = new Impl_Dosomething();

Dezavantajı, uygulama sınıfının hala sabit kodlanmış olmasıdır, bu nedenle ön uç hangi uygulamanın kullanıldığı bilgisine sahiptir. DI, tasarımı arayüzle bir adım öteye götürür, ön ucun bilmesi gereken tek şey arayüz bilgisidir. DYI ve DI arasında bir servis bulucu deseni vardır, çünkü ön uç, isteğinin çözülmesini sağlamak için bir anahtar (servis bulucunun kayıt defterinde bulunur) sağlamak zorundadır. Servis bulucu örneği:

I_Dosomething x = ServiceLocator.returnDoing(String pKey);

DI örneği:

I_Dosomething x = DIContainer.returnThat();

DI'nin gerekliliklerinden biri, kabın hangi sınıfın hangi arabirimin uygulanması olduğunu öğrenebilmesidir. Bu nedenle, bir DI konteyneri, güçlü bir şekilde yazılmış tasarım ve aynı anda her arabirim için yalnızca bir uygulama gerektirir. Aynı anda daha fazla arayüz uygulamasına ihtiyacınız varsa (hesap makinesi gibi), servis bulucuya veya fabrika tasarım modeline ihtiyacınız vardır.

D (b) I: Bağımlılık Enjeksiyonu ve Arayüz ile Tasarım. Bu kısıtlama çok büyük bir pratik problem değildir. D (b) I kullanmanın yararı, müşteri ile sağlayıcı arasında iletişime hizmet etmesidir. Arayüz, bir nesneye veya bir davranış kümesine perspektiftir. İkincisi burada çok önemlidir.

Kodlamada D (b) I ile birlikte hizmet sözleşmelerinin uygulanmasını tercih ederim. Birlikte gitmeliler. D (b) I'in hizmet sözleşmelerinin örgütsel yönetimi olmadan teknik bir çözüm olarak kullanılması benim görüşüme göre çok yararlı değil, çünkü DI o zaman sadece ekstra bir kapsülleme katmanı. Ancak bunu örgütsel yönetim ile birlikte kullanabildiğinizde, sunduğum D (b) organizasyon ilkesini gerçekten kullanabilirsiniz. Test, versiyon oluşturma ve alternatiflerin geliştirilmesi gibi konularda müşteri ve diğer teknik departmanlarla iletişimi yapılandırmak için uzun vadede size yardımcı olabilir. Eğer sabit kodlanmış bir sınıfta olduğu gibi örtük bir arayüze sahipseniz, o zaman D (b) I kullanarak açık hale getirdiğinizde zamanla çok daha az bulaşıcıdır. Her şey bir anda değil, zaman içinde olan bakıma kaynar. :-)


1
"Dezavantajı uygulama sınıfı hala sabit kodlanmış" <- çoğu zaman sadece tek bir uygulama var ve dediğim gibi zaten yerleşik olmayan bir arayüz gerektiren bir isim adı kodu düşünemiyorum (.NET ).

@ acidzombie24 Olabilir ... ancak DI'yi kullanarak bir çözüm uygulama çabasını, arayüzlere ihtiyacınız varsa daha sonra DI olmayan bir çözümü değiştirme çabasıyla karşılaştırın. Neredeyse her zaman ilk seçenekle giderdim. Yarın 100.000 $ ödemek yerine şimdi 100 $ ödemek daha iyi.
Golo Roden

1
@GoloRoden Aslında, bakım, D (b) I gibi teknikleri kullanan temel konudur. Bu, bir başvuru maliyetinin% 80'i kadardır. Başlangıçtan itibaren arayüzler kullanılarak gerekli davranışın açıkça ortaya konduğu bir tasarım, organizasyonu daha sonra çok zaman ve para tasarrufu sağlar.
Loek Bergman

Ben ödemek zorunda kalana kadar gerçekten anlamayacağım çünkü şimdiye kadar 0 $ ödedi ve şimdiye kadar hala sadece 0 $ ödemek gerekiyor. Ama her satırı veya işlevi temiz tutmak için 0.05 $ ödüyorum.
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.