Che cos'è l'iniezione del costruttore?

42

Ho esaminato i termini iniezione del costruttore e iniezione della dipendenza mentre si passava attraverso gli articoli sui modelli di progettazione (Service locator).

Quando ho cercato su google l'iniezione del costruttore, ho ottenuto risultati non chiari, il che mi ha spinto a registrarmi qui.

Che cos'è l'iniezione del costruttore? Si tratta di un tipo specifico di iniezione di dipendenza? Un esempio canonico sarebbe di grande aiuto!

Modifica

Rivedendo queste domande dopo un'interruzione di una settimana, posso vedere quanto sono stato perso ... Nel caso in cui qualcun altro entri qui, aggiornerò il corpo della domanda con un mio piccolo apprendimento. Per favore sentiti libero di commentare / correggere. L'iniezione del costruttore e l'iniezione di proprietà sono due tipi di Iniezione delle dipendenze.

    
posta TheSilverBullet 29.11.2012 - 11:34
fonte

3 risposte

88

Non sono esperto, ma penso di poterti aiutare. E sì, è un tipo specifico di Iniezione di Dipendenza.

Dichiarazione di non responsabilità: quasi tutto ciò è stato "rubato" da Ninject Wiki

Esaminiamo l'idea dell'iniezione di dipendenza seguendo un semplice esempio. Diciamo che stai scrivendo il prossimo gioco di successo, dove i nobili guerrieri combattono per la grande gloria. Per prima cosa, avremo bisogno di un'arma adatta per armare i nostri guerrieri.

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

Quindi, creiamo un corso per rappresentare i nostri guerrieri stessi. Per attaccare i suoi nemici, il guerriero avrà bisogno di un metodo di attacco (). Quando viene chiamato questo metodo, dovrebbe usare la sua Spada per colpire il suo avversario.

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

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

Ora possiamo creare il nostro samurai e combattere!

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

Come puoi immaginare, questo stamperà Taglia i malfattori puliti a metà alla console. Funziona bene, ma se volessimo armare il nostro samurai con un'altra arma? Poiché la Spada viene creata all'interno del costruttore della classe Samurai, dobbiamo modificare l'implementazione della classe per apportare questa modifica.

Quando una classe dipende da una dipendenza concreta, si dice che sia strettamente accoppiata a quella classe . In questo esempio, la classe Samurai è strettamente accoppiata alla classe Sword. Quando le classi sono strettamente accoppiate, non possono essere interscambiate senza alterare la loro implementazione. Per evitare classi di accoppiamento stretto, possiamo utilizzare le interfacce per fornire un livello di riferimento indiretto. Creiamo un'interfaccia per rappresentare un'arma nel nostro gioco.

interface IWeapon
{
    void Hit(string target);
}

Quindi, la nostra classe Sword può implementare questa interfaccia:

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

E possiamo modificare la nostra classe Samurai:

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

Ora il nostro Samurai può essere armato con diverse armi. Ma aspetta! La spada è ancora creata all'interno del costruttore di Samurai. Poiché abbiamo ancora bisogno di modificare l'implementazione dei Samurai per dare al nostro guerriero un'altra arma, Samurai è ancora strettamente accoppiato a Sword.

Fortunatamente, esiste una soluzione semplice. Piuttosto che creare la Spada dall'interno del costruttore di Samurai, possiamo esporla invece come parametro del costruttore. Conosciuto anche come Iniezione costruttore.

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

Come ha sottolineato Giorgio, c'è anche un'iniezione di proprietà. Sarebbe qualcosa come:

class Samurai
{
    IWeapon weapon;

    public Samurai() { }


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

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

}

Spero che questo aiuti.

    
risposta data 29.11.2012 - 12:44
fonte
3

Proverò il più possibile a rendere questo molto elementare. In questo modo, puoi sviluppare il concetto e creare le tue soluzioni o idee complesse.

Ora immaginiamo di avere una chiesa chiamata Giubileo, che ha diramazioni in tutto il mondo. Il nostro obiettivo è semplicemente ottenere un oggetto da ogni ramo. Qui sarebbe la soluzione con DI;

1) Crea un'interfaccia IJubilee:

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

2) Creare un JubileeDI di classe che utilizzi l'interfaccia IJubilee come costruttore e restituisca un elemento:

public class JubileeDI
{
    readonly IJubilee jubilee;

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

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

3) Ora crea tre braches del Giubileo, vale a dire JubileeGT, JubileeHOG, JubileeCOV, che tutti DEVONO ereditare l'interfaccia IJubilee. Per il gusto di farlo, fai in modo che uno di loro realizzi il suo metodo come virtuale:

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) È così! Ora vediamo DI in azione:

        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!");

Per ogni istanza della classe contenitore, passiamo una nuova istanza della classe che richiediamo, che consente l'accoppiamento lento.

Spero che questo spieghi il concetto in poche parole.

    
risposta data 02.08.2013 - 20:04
fonte
2

Supponi di avere una classe A ogni istanza di cui necessita un'istanza di un'altra classe B .

class A
{
   B b;
}

Iniezione di dipendenza significa che il riferimento a B è impostato dall'oggetto che gestisce l'istanza di A (anziché avere la classe A che gestisce il riferimento a B direttamente).

Iniezione del costruttore significa che il riferimento a B viene passato come parametro al costruttore di A e impostato nel costruttore:

class A
{
    B b;

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

Un'alternativa è usare un metodo setter (o una proprietà) per impostare il riferimento a B . In questo caso, l'oggetto che gestisce un'istanza a di A deve prima chiamare il costruttore di A e successivamente chiamare il metodo setter per impostare la variabile membro A.b prima che venga utilizzato a . L'ultimo metodo di iniezione è necessario quando gli oggetti contengono riferimenti ciclici l'uno con l'altro, ad es. se un'istanza b di B impostata in un'istanza a di A contiene un riferimento a a .

** MODIFICA **

Ecco alcuni altri dettagli per indirizzare il commento.

1. La classe A gestisce l'istanza B

class A
{
    B b;

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

2. L'istanza B è impostata nel costruttore

class A
{
    B b;

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

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

3. L'istanza B viene impostata utilizzando il metodo setter

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);
}
    
risposta data 29.11.2012 - 11:53
fonte

Leggi altre domande sui tag