Yapıcı enjeksiyon nedir?


47

(Servis bulucu) tasarım kalıpları üzerine yazılar yazarken, yapıcı enjeksiyon ve bağımlılık enjeksiyon terimlerini inceliyorum.

Yapıcı enjeksiyon hakkında göz gezdirdiğimde belirsiz sonuçlar aldım, bu da burada kontrol etmemi istedi.

Yapıcı enjeksiyon nedir? Bu spesifik bir bağımlılık enjeksiyonu türü mü? Bir kanonik örnek çok yardımcı olacaktır!

Düzenle

Bir hafta ara verdikten sonra bu soruları tekrar ziyaret ederek, ne kadar kaybolduğumu görebiliyorum ... Sadece burada birisinin ortaya çıkması durumunda, soru bedenini benimkileri öğrenerek güncelleyeceğim. Lütfen yorum yapmaktan / düzeltmekten çekinmeyin. Yapıcı enjeksiyon ve özellik enjeksiyonu iki tip Bağımlılık Enjeksiyonu şeklindedir.


5
Google'da ilk hit açıkça açıklıyor ... misko.hevery.com/2009/02/19/…
user281377

Yanıtlar:


95

Uzman değilim ama yardım edebileceğimi düşünüyorum. Ve evet, belirli bir Bağımlılık Enjeksiyonu türüdür.

Feragatname: Neredeyse bunların tamamı Ninject Wiki'den "çalındı"

Bağımlılık enjeksiyonu fikrini basit bir örnek kullanarak yürüterek inceleyelim. Diyelim ki sıradaki gişe rekorları kıran oyunu yazıyorsunuz, soylu savaşçıların büyük zafer için savaş yaptıkları yer. İlk önce, savaşçılarımızı silahlandırmaya uygun bir silaha ihtiyacımız olacak.

class Sword 
{
    public void Hit(string target)
    {
        Console.WriteLine("Chopped {0} clean in half", target);
    }
}

Öyleyse savaşçılarımızı kendileri temsil edecek bir sınıf oluşturalım. Düşmanlarına saldırmak için savaşçı bir Attack () yöntemine ihtiyaç duyacak. Bu yöntem çağrıldığında, rakibini vurmak için Kılıcını kullanmalıdır.

class Samurai
{
    readonly Sword sword;
    public Samurai() 
    {
        this.sword = new Sword();
    }

    public void Attack(string target)
    {
        this.sword.Hit(target);
    }
}

Şimdi, Samuray'larımızı yaratabilir ve savaş yapabiliriz!

class Program
{
    public static void Main() 
    {
        var warrior = new Samurai();
        warrior.Attack("the evildoers");
    }
}

Tahmin edebileceğiniz gibi, bu yazdıracak Doğranmış köpeği konsola yarısında temizleyin . Bu iyi çalışıyor, ama ya Samuray'larımızı başka bir silahla silahlandırmak istersek? Kılıç, Samuray sınıfının kurucusu içinde yaratıldığından, bu değişikliği yapabilmek için sınıfın uygulamasını değiştirmeliyiz.

Bir sınıf somut bir bağımlılığa bağlı olduğunda, o sınıfa sıkı bir şekilde bağlı olduğu söylenir . Bu örnekte, Samuray sınıfı, Kılıç sınıfına sıkıca bağlanmıştır. Sınıflar sıkıca birleştirildiğinde, uygulamalarını değiştirmeden değiştirilemezler. Sıkı kavrama sınıflarından kaçınmak için, bir dolaylı seviye sağlamak için arayüzleri kullanabiliriz. Oyunumuzda bir silahı temsil edecek bir arayüz oluşturalım.

interface IWeapon
{
    void Hit(string target);
}

Daha sonra Kılıç sınıfımız bu arayüzü uygulayabilir:

class Sword : IWeapon
{
    public void Hit(string target) 
    {
        Console.WriteLine("Chopped {0} clean in half", target);
    }
}

Samuray sınıfımızı değiştirebiliriz:

class Samurai
{
    readonly IWeapon weapon;
    public Samurai() 
    {
        this.weapon = new Sword();
    }
    public void Attack(string target) 
    {
        this.weapon.Hit(target);
    }
}

Şimdi Samuraylarımız farklı silahlarla silahlandırılabilir. Fakat bekle! Kılıç, hala Samuray'ın kurucusu içinde yaratılmıştır. Savaşçımıza başka bir silah vermek için Samuray'ın uygulamasını değiştirmeye devam etmemiz gerektiğinden, Samuray hala Kılıç'la sıkı sıkıya bağlantılı.

Neyse ki, kolay bir çözüm var. Kılıcı Samurayın kurucusundan yaratmak yerine, onu yapıcı parametresi olarak gösterebiliriz. Ayrıca Yapıcı Enjeksiyonu olarak da bilinir.

class Samurai
{
    readonly IWeapon weapon;
    public Samurai(IWeapon weapon) 
    {
        this.weapon = weapon;
    }
    public void Attack(string target) 
    {
        this.weapon.Hit(target);
    }
}

Giorgio'nun belirttiği gibi, mülk enjeksiyonu da var. Bu gibi bir şey olurdu:

class Samurai
{
    IWeapon weapon;

    public Samurai() { }


    public void SetWeapon(IWeapon weapon)
    {
        this.weapon = weapon;
    }

    public void Attack(string target) 
    {
        this.weapon.Hit(target);
    }

}

Bu yardımcı olur umarım.


8
Bunu sevdim! :) Aslında bir hikaye gibi okudum! Sadece merak. Aynı Samurai'yi birden fazla silahla (sırayla) silahlandırmak isteseydiniz, mülk enjeksiyonu en iyi yol olur mu?
TheSilverBullet

Evet, bunu yapabilirsin. Aslında, IWeapon'ı Attack yöntemine parametre olarak geçirmenin daha iyi olacağını düşünüyorum. "Genel geçersiz Saldırı (IWeapon with, string target)" gibi. Ve yinelenen kodlardan kaçınmak için mevcut yöntemi "public void Attack (string target)" bozulmamış ve "this.Attack (this.weapon, target)" olarak adlandırırım.
Luiz Angelo,

Daha sonra fabrikalarla birleştiğinde, CreateSwordSamurai () gibi yöntemler elde edebilirsiniz! Güzel örnek
Geerten

2
Harika bir örnek! Çok açık ...
Serge van den Oever

@Luiz Angelo. Maalesef, yorumunuzu anladığımdan emin değilim: "Aslında, IWeapon'ı Attack yöntemine parametre olarak geçirmekten daha iyi hizmet edeceğinizi düşünüyorum.". Samuray sınıfında Attack yöntemini aşırı mı demek istiyorsun?
Pap

3

Bunu çok basit hale getirmek için mümkün olduğunca çok çalışacağım. Bu şekilde, konsepti geliştirebilir ve kendi karmaşık çözümlerinizi veya fikirlerinizi oluşturabilirsiniz.

Şimdi, dünya çapında şubeleri bulunan Jubilee adında bir kilisemiz olduğunu hayal edin. Amacımız her şubeden basit bir ürün elde etmektir. İşte DI ile çözüm olurdu;

1) Bir arayüz oluşturun IJubilee:

public interface IJubilee
{
    string GetItem(string userInput);
}

2) IJubilee arayüzünü kurucu olarak alan ve bir eşya döndüren JubileeDI sınıfı oluşturun:

public class JubileeDI
{
    readonly IJubilee jubilee;

    public JubileeDI(IJubilee jubilee)
    {
        this.jubilee = jubilee;
    }

    public string GetItem(string userInput)
    {
        return this.jubilee.GetItem(userInput);
    }
}

3) Şimdi Jubilee arayüzünü miras alması GEREKEN Jubilee GT, JubileeHOG, JubileeCOV adlı üç küme oluşturun. Bunun eğlencesi için, birisinin yöntemini sanal olarak uygulamasını sağlayın:

public class JubileeGT : IJubilee
{
    public virtual string GetItem(string userInput)
    {
        return string.Format("For JubileeGT, you entered {0}", userInput);
    }
}

public class JubileeHOG : IJubilee
{
    public string GetItem(string userInput)
    {
        return string.Format("For JubileeHOG, you entered {0}", userInput);
    }
}

public class JubileeCOV : IJubilee
{
    public string GetItem(string userInput)
    {
        return string.Format("For JubileCOV, you entered {0}", userInput);
    }
}

public class JubileeGTBranchA : JubileeGT
{
    public override string GetItem(string userInput)
    {
        return string.Format("For JubileeGT branch A, you entered {0}", userInput);
    }
}

4) İşte bu! Şimdi DI'yi iş başında görelim:

        JubileeDI jCOV = new JubileeDI(new JubileeCOV());
        JubileeDI jHOG = new JubileeDI(new JubileeHOG());
        JubileeDI jGT = new JubileeDI(new JubileeGT());
        JubileeDI jGTA = new JubileeDI(new JubileeGTBranchA());

        var item = jCOV.GetItem("Give me some money!");
        var item2 = jHOG.GetItem("Give me a check!");
        var item3 = jGT.GetItem("I need to be fed!!!");
        var item4 = jGTA.GetItem("Thank you!");

Konteynır sınıfının her bir örneği için, ihtiyacımız olan sınıfın yeni bir örneğini geçtik, ki bu da gevşek bağlanma sağlar.

Umarım bu kısaca kavramı açıklar.


2

AHer birinin başka bir sınıfın örneğini gerektiren bir sınıfınız olduğunu varsayalım B.

class A
{
   B b;
}

Bağımlılık enjeksiyonu, referansın Börneğini yöneten nesne tarafından ayarlandığı anlamına gelir A(sınıfı Areferansı Bdoğrudan yöneten sınıfın aksine ).

Yapıcı enjeksiyonu, referansın Byapıcıya bir parametre olarak iletildiği ve yapıcıda Aayarlandığı anlamına gelir :

class A
{
    B b;

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

Alternatif, referansı ayarlamak için bir ayarlayıcı yöntemi (veya özellik) kullanmaktır B. Bu durumda, bir örneğini yöneten nesne aarasında Amutlaka ilk kurucusunu çağırmak Ave daha sonra üye değişkeni ayarlamak için ayarlayıcı yöntemini çağırmak A.bönce akullanılır. Bir örneği, eğer nesne, örneğin, birbirine siklik referanslar ihtiva ettiğinde ikinci enjeksiyon yöntemi gerekli barasında Bolduğu bir örneği yer almaktadır ave Abir arka referans içerir a.

** DÜZENLE**

Yoruma değinmek için biraz daha detay.

1. A sınıfı B örneğini yönetir

class A
{
    B b;

    A()
    {
        b = new B();
    }
 }

2. B örneği yapıcıda ayarlanır

class A
{
    B b;

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

class M
{
    ...
    B b = new B();
    A a = new A(b);
}

3. B örneği ayarlayıcı yöntemi kullanılarak ayarlanır

class A
{
    B b;

    A()
    {
    }

    void setB(B b)
    {
        this.b = b;
    }
}

class M
{
    ...
    B b = new B();
    A a = new A();

    a.setB(b);
}

... A sınıfının doğrudan B referansını yönetmesini sağlamak . Ne anlama geliyor? Kodda nasıl yaparsın? (Pardon cehaletim.)
TheSilverBullet

1
Samuray örneği çok daha iyi.
Hepimiz

@stuartdotnet: Samuray örneğinin daha kötüye gitmesi konusunda tartışmıyorum ama neden bir harfli değişkenleri kullanmayı bıraktınız? Değişkenlerin belirli bir anlamı yoksa yalnızca yer tutucuları varsa, tek harfli değişkenler çok daha kompakt ve söz konusudur. Gibi itemAya da objectAsadece daha uzun isimler kullanmak için daha uzun isimler kullanmak her zaman daha iyi değildir.
Giorgio

1
O zaman benim
fikrimi özledin

@stuartdotnet: Amacını kaçırdığımı sanmıyorum: Açıklayıcı isimlerin her zaman daha iyi olduğunu ve kodun her zaman daha okunaklı ve kendi kendini açıkladığını iddia ettin.
Giorgio
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.