Vendimi su contenitori IoC, per favore


17

Ho visto diversi raccomandare l'uso di contenitori IoC nel codice. La motivazione è semplice Prendi il seguente codice iniettato di dipendenza:

class UnitUnderTest
{
    std::auto_ptr<Dependency> d_;
public:
    UnitUnderTest(
        std::auto_ptr<Dependency> d = std::auto_ptr<Dependency>(new ConcreteDependency)
    ) : d_(d)
    {
    }
};


TEST(UnitUnderTest, Example)
{
    std::auto_ptr<Dependency> dep(new MockDependency);
    UnitUnderTest uut(dep);
    //Test here
}

In:

class UnitUnderTest
{
    std::auto_ptr<Dependency> d_;
public:
    UnitUnderTest()
    {
        d_.reset(static_cast<Dependency *>(IocContainer::Get("Dependency")));
    }
};


TEST(UnitUnderTest, Example)
{
    UnitUnderTest uut;
    //Test here
}

//Config for IOC container normally
<Dependency>ConcreteDependency</Dependency>

//Config for IOC container for testing
<Dependency>MockDependency</Dependency>

(Quanto sopra è ovviamente un esempio ipotetico di C ++)

Mentre sono d'accordo sul fatto che ciò semplifichi l'interfaccia della classe rimuovendo il parametro del costruttore di dipendenza, penso che la cura sia peggiore della malattia per un paio di motivi. Innanzitutto, e questo è importante per me, questo rende il tuo programma dipendente da un file di configurazione esterno. Se è necessaria una distribuzione binaria singola, semplicemente non è possibile utilizzare questo tipo di contenitori. Il secondo problema è che l'API è ora debolmente e peggio ancora, tipizzata in modo rigoroso . L'evidenza (in questo ipotetico esempio) è l'argomento stringa al contenitore IoC e il cast sul risultato.

Quindi ... ci sono altri vantaggi nell'uso di questi tipi di contenitori o non sono d'accordo con quelli che raccomandano i contenitori?


1
Il codice di esempio sembra un esempio davvero negativo di un'implementazione IoC. Conosco solo C # ma sicuramente c'è un modo per avere l'iniezione del costruttore e la configurazione programmatica anche in C ++?
rmac,

Risposte:


12

In una grande applicazione con molti strati e molte parti mobili, gli svantaggi iniziano a sembrare piuttosto minori rispetto ai vantaggi.

Il contenitore "semplifica l'interfaccia" della classe, ma lo fa in un modo molto importante. Il contenitore è una soluzione al problema creato dall'iniezione delle dipendenze, ovvero la necessità di passare le dipendenze ovunque, attraverso i grafici degli oggetti e le aree funzionali. Hai un piccolo esempio qui che ha una dipendenza: cosa succederebbe se questo oggetto avesse tre dipendenze e gli oggetti che dipendevano da esso avevano più oggetti che dipendevano da loro , e così via? Senza un contenitore, gli oggetti nella parte superiore di tali catene di dipendenze finiscono per diventare responsabili per tenere traccia di tutte le dipendenze nell'intera applicazione.

Esistono anche diversi tipi di contenitori. Non tutti sono digitati in modo rigoroso e non tutti richiedono file di configurazione.


3
Ma un grande progetto rende i problemi che ho già menzionato peggio . Se il progetto è di grandi dimensioni, il file di configurazione diventa un enorme magnete di dipendenza stesso e diventa ancora più semplice avere un errore di battitura che porta a comportamenti indefiniti in fase di esecuzione. (vale a dire che avere un refuso nel file di configurazione provocherebbe un comportamento indefinito nel cast se viene restituito il tipo sbagliato). Per quanto riguarda le dipendenze delle dipendenze, quelle non contano qui. O il costruttore si prende cura di farli, o il test prenderà in giro la dipendenza di livello superiore.
Billy ONeal,

... Continua ... Quando il programma non viene testato, la dipendenza concreta predefinita viene istanziata ad ogni passaggio. Hai scritto altri tipi di container, hai qualche esempio?
Billy ONeal,

4
TBH Non ho molta esperienza con le implementazioni DI, ma dai un'occhiata a MEF su .NET, che può essere ma non deve essere tipizzato in modo stringente, non ha file di configurazione e le implementazioni concrete di interfacce sono "registrate" sulle classi stesse. A proposito, quando dici: "il costruttore si prende cura di farli" - se è quello che stai facendo (aka "iniezione del costruttore del povero"), allora non hai bisogno di un contenitore. Il vantaggio di un contenitore rispetto a ciò è che il contenitore centralizza le informazioni su tutte quelle dipendenze.
nlawalker,

+1 per l'ultimo commento - l'ultima frase c'è la risposta :)
Billy ONeal

1
Ottima risposta che mi ha aiutato a capire tutto. Il punto non è solo semplificare la piastra della caldaia, ma consentire agli elementi nella parte superiore della catena di dipendenza di essere altrettanto ingenui degli elementi nella parte inferiore.
Christopher Berman,

4

Il framework Guice IoC di Java non si basa su un file di configurazione ma sul codice di configurazione . Ciò significa che la configurazione non è un codice diverso dal codice che costituisce l'applicazione effettiva e può essere riformattata ecc.

Credo che Guice sia il framework IoC Java che alla fine ha ottenuto la configurazione corretta.


Ci sono esempi simili per C ++?
Billy ONeal,

@Billy, non ho abbastanza familiarità con il C ++ per poterlo dire.

0

Questa grande risposta SO di Ben Scheirman descrive alcuni esempi di codice in C #; alcuni vantaggi dei contenitori IoC (DI) sono:

  • La catena delle dipendenze può essere nidificata e diventa rapidamente ingombrante collegarle manualmente.
  • Consente l'uso della programmazione orientata all'aspetto .
  • Transazioni di database dichiarative e nidificate.
  • Unità di lavoro dichiarativa e nidificata.
  • Registrazione.
  • Condizioni pre / post (progettazione per contratto).

1
Non vedo come nessuna di queste cose non possa essere implementata con la semplice iniezione del costruttore.
Billy ONeal,

Con una buona progettazione, l'iniezione del costruttore o un altro metodo potrebbero essere sufficienti per te; i vantaggi di questi contenitori IoC potrebbero essere solo in casi specifici. L'esempio fornito con INotifyPropertyChanged in un progetto WPF ne è stato un esempio.
dodgy_coder il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.