Che cosa vuol dire Dependency Injection dicendo che può essere iniettato in runtime?

1

Capisco che l'iniezione delle dipendenze è qualcosa fatto dalla codifica e tutto è fatto in fase di compilazione. E quella codifica dell'iniezione delle dipendenze è ora facilmente gestita da strumenti di supporto come Ninject.

Tuttavia è in fase di compilazione che si verifica l'iniezione della dipendenza. Quindi cosa significa che l'iniezione di dipendenza si verifica anche in runtime?

Questo è direttamente dalla wiki su Dependency Injection

Dependency injection is a software design pattern that allows the removal of hard-coded dependencies and makes it possible to change them, whether at run-time or compile-time.

Voglio dire ci ho pensato e mi è venuta l'idea che possa riferirsi a più interfacce a una singola classe quando un delegato chiama un metho da una delle interfacce, l'injection dependence decide qualcosa di simile in fase di runtime, ma quando è andato più in profondità e anche discusso con il mio capo sembrava non è così semplice. E allora, in che modo l'iniezione di dipendenza prende questa decisione e la assume come corretta?

Chiunque può aiutarmi a risolvere questa domanda?

    
posta Steven Hernandez 20.11.2013 - 02:16
fonte

5 risposte

11

However this is at compile time that the dependency injection occurs.

Il tipo di servizio è definito al momento della compilazione. Questo è normalmente sotto forma di un'interfaccia o di una classe di base (a volte astratta). Il punto chiave qui è principio di sostituzione di Liskovs

It states that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may be substituted for objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.)

Quindi conosciamo il contratto del tipo in fase di compilazione

Then what does it mean that dependency injection also occurs at Runtime?

Quando si implementa DI, ad esempio, utilizzando un contenitore Inversion of Control il tipo di implementazione non è noto fino al runtime.

Esaminiamo un esempio di base per preparare un caffè usando Iniettore semplice come contenitore Inversion of Control e NUnit per testare il codice.

Per prima cosa definiamo la tazza e il nostro tipo di servizio:

public class Cup 
{
    public List<string> ingredients = new List<string>();
}

public interface ICoffeeService
{
    Cup GetCoffee();
}

E un'implementazione del servizio:

public class CoffeeService : ICoffeeService
{
    public Cup GetCoffee()
    {
        Cup cup = new Cup();
        cup.ingredients.Add("Coffee powder");
        cup.ingredients.Add("Hot water");
        return cup;
    }
}

Il nostro metodo di test configura il contenitore per restituire CoffeeService quando viene richiesta un'istanza di ICoffeeService

[Test]
public void CoffeeService_GetCoffee_ReturnsCupWithCoffeeAndHotWater()
{
    // Arrange
    var container = new SimpleInjector.Container();
    container.Register<ICoffeeService, CoffeeService>();

    // Act
    var coffeeService = container.GetInstance<ICoffeeService>();
    var cup = coffeeService.GetCoffee();

    // Assert
    Assert.That(cup.ingredients.Count == 2);
    Assert.That(cup.ingredients.Contains("Coffee powder"));
    Assert.That(cup.ingredients.Contains("Hot water"));
}

Decidiamo di dover aggiungere sempre crema al nostro caffè, ma invece di cambiare CoffeeService invece Decorare con la nuova funzione ( principio Aperto / Chiuso ).

Il decoratore

public class AddCreamDecorator : ICoffeeService
{
    private readonly ICoffeeService decorated;

    public AddCreamDecorator(ICoffeeService decorated)
    {
        this.decorated = decorated;
    }

    public Cup GetCoffee()
    {
        Cup cup = this.decorated.GetCoffee();
        cup.ingredients.Add("Cream");
        return cup;
    }
}

E il nuovo test

[Test]
public void DecoratedCoffeeService_GetCoffee_CupWithCoffeeAndHotWaterAndCream()
{
    // Arrange
    var container = new SimpleInjector.Container();
    container.Register<ICoffeeService, CoffeeService>();
    container.RegisterDecorator(typeof(ICoffeeService), typeof(AddCreamDecorator));

    // Act
    var coffeeService = container.GetInstance<ICoffeeService>();
    var cup = coffeeService.GetCoffee();

    // Assert
    Assert.That(cup.ingredients.Count == 3);
    Assert.That(cup.ingredients.Contains("Coffee powder"));
    Assert.That(cup.ingredients.Contains("Hot water"));
    Assert.That(cup.ingredients.Contains("Cream"));
}

Il punto più importante da prendere da questo esempio è che eseguiamo il codice e compiliamo il tipo di servizio ICoffeeService e il contenitore ci fornisce l'implementazione di runtime configurata. Nel test uno otteniamo un'istanza di CoffeeService mentre nel secondo test otteniamo un'istanza di CoffeeService decorata con un'istanza di AddCreamDecorator - in entrambi i casi il chiamante non si cura di ottenere il servizio di cui ha bisogno .

    
risposta data 20.11.2013 - 11:49
fonte
4

Iniezione di dipendenza significa semplicemente che si passa in una o più implementazioni dell'interfaccia, in genere al costruttore della classe. I metodi di classe quindi chiamano a loro volta metodi su queste interfacce piuttosto che richiamare ad es. direttamente a una libreria.

Quando si esegue questa operazione è possibile rinviare la decisione circa esattamente quale implementazione si desidera passare (iniettare) fino al momento dell'esecuzione in cui la decisione su quale particolare implementazione dovrebbe essere utilizzata potrebbe, ad esempio, essere configurata attraverso un file di configurazione. / p>     

risposta data 20.11.2013 - 02:26
fonte
2

La tua comprensione di Dipendenza da Iniezione è corretta e quando lo fai codificandolo sarà fatto in fase di compilazione. Per avere le dipendenze iniettate in fase di esecuzione è necessario Contenitore di iniezione delle dipendenze (Provider) . Fondamentalmente fa la codifica per te, devi solo configurarlo in modo che possa passare i parametri giusti.

    
risposta data 20.11.2013 - 05:29
fonte
2

Sono in ritardo per la festa, ma ho notato che nessun altro ha risposto alla domanda vera e propria. Ecco un semplice esempio di DI runtime:

Per prima cosa hai bisogno di un oggetto con una dipendenza:

public MyRoom(ILightFixture fixture)
{
  this.MyLightFixture = fixture ; 
}

E poi, nella tua root di composizione, includi una decisione in fase di esecuzione:

public void Main()
{
  // make runtime decision (e.g. input from user); respond dynamically...
  ...
  if (userChoice == "Classy")
     room = new MyRoom(new ClassyChandelier());
  else
     room = new MyRoom(new SubduedSconce());
}

Semplice come quello. Ora l'umore del tuo oggetto room è determinato dinamicamente (in fase di esecuzione).

    
risposta data 18.03.2014 - 19:00
fonte
1

Non ho usato Ninject in un progetto da solo, ma ho familiarità con esso, quindi proverò ad usarlo per un esempio.

Una dipendenza che viene modificata in fase di compilazione sarebbe quella che è esplicitamente configurata nel codice che viene eseguito all'avvio del programma. per esempio. per i documenti di Ninject:

Bind<IWeapon>().To<Sword>();

Una dipendenza modificata al momento dell'esecuzione sarebbe quella configurata per utilizzare un provider, piuttosto che un tipo concreto, ad es.

Bind<IWeapon>().ToProvider(new SwordProvider());

Quindi, la classe SwordProvider potrebbe fornire un'implementazione CreateInstance() che, ad esempio, colpisce un database per determinare quale classe concreta fornire. Quindi, il comportamento potrebbe essere modificato in fase di esecuzione aggiornando il database. O qualsiasi altra cosa.

    
risposta data 20.11.2013 - 08:16
fonte

Leggi altre domande sui tag