Configurazione di alto livello di Iniezione del costruttore in C ++

0

Le mie domande riguardano specificamente l'iniezione delle dipendenze attraverso il costruttore. Comprendo i pro / contro del modello di localizzatore di servizi, dell'iniezione costruttore / setter e dei loro aromi, tuttavia c'è qualcosa che non riesco a superare dopo aver scelto l'iniezione pura del costruttore. Dopo aver letto molti materiali per il design verificabile, incluso un approfondito esame del blog di Miško Hevery (in particolare questo post) Sono nella seguente situazione:

Supponiamo che io stia scrivendo un programma C ++ e ho iniettato correttamente le mie dipendenze attraverso i loro costruttori. Per la leggibilità mi sono dato un oggetto di alto livello che ha una singola funzione Execute () chiamata da main:

int main(int argc, char* argv[]) {
    MyAwesomeProgramObject object(argc, argv);
    return object.Execute();
}

La responsabilità di Execute () è di collegare semplicemente tutti gli oggetti richiesti e dare il via all'oggetto di livello più alto. L'oggetto di livello più alto richiede un paio di dipendenze e quegli oggetti richiedono alcuni oggetti e così via e così via, implicando una funzione simile a questa:

MyAwesomeProgramObject::Execute() {
    DependencyOne one;
    DependencyTwo two;
    DependencyThree three;

    MidLevelOne mid_one(one);
    MidLevelTwo mid_two(two, three);

    // ...

    MidLevelN mid_n(mid_dependencyI, mid_dependencyJ, mid_dependencyK);

    // ...

    HighLevelObject1 high_one(mid_one, mid_n);
    HighLevelObject2 high_two(mid_two);

    ProgramObject object(high_one, high_two);
    return object.Go();
}

Da quello che prendo dal blog di Miško (e glielo chiedo, ma ho pensato che non avrebbe avuto il tempo di tornare da me), questo è l'unico modo per soddisfare l'iniezione pura delle dipendenze del costruttore.

Nel post del blog menzionato , afferma che dovremmo avere fabbriche su un livello di vita per oggetto, ma questo è essenzialmente ciò che Execute sta facendo, rendendo il mio codice simile al suo esempio:

AuditRecord audit = new AuditRecord();
Database database = new Database(audit);
Captcha captcha = new Captcha();
Authenticator authenticator =
    new Authenticator(database, captcha, audit);
LoginPage = new LoginPage(audit, authenticator);

Domande:

  • È questo l'approccio standard?
  • Si tratta di un pattern di cui non sono a conoscenza (sembra simile al contesto.xml di Maven)?
  • Per l'iniezione pura del costruttore, devo semplicemente subire il costo dell'allocazione "anticipata"?
posta Danny A 27.11.2013 - 17:41
fonte

1 risposta

1

Is this the standard approach?

Non esiste un "approccio standard", in particolare in C ++. In realtà, questo tipo di scelta è specifica per il design di cui hai bisogno per risolvere i tuoi problemi.

Fondamentalmente, quello che devi chiedere è "quando devo inizializzare questa dipendenza?" e devi chiederlo per ogni dipendenza. Se è possibile inizializzare tutto in fase di costruzione, quindi, è possibile evitare chiamate / nuove chiamate non necessarie, il che rende più semplice il trattamento del programma. Se hai davvero bisogno di avere alcune dipendenze inizializzate solo sulla chiamata di esecuzione, allora non hai scelta, fallo in questo momento.

Tuttavia, suggerirei di evitare / eliminare il più possibile, se possibile, ad esempio inserendo le dipendenze di inizializzazione tardiva in un altro oggetto che rappresenterebbe la loro durata, forse chiamato "ExecutionDependencies" o qualcosa, gestito da una smart sottolineato (std :: unique_ptr è sicuramente la scelta migliore). È equivalente ad avere diverse chiamate new / delete ma lo fai solo una volta per l'intero oggetto che contiene tutte le dipendenze di esecuzione. È quindi facile rilasciare successivamente le dipendenze necessarie solo per l'esecuzione e mantenere le dipendenze necessarie per la durata del programma whold.

Detto questo, potrebbe non essere molto importante, quindi francamente scriverò prima il modo più semplice, poi il refattore più avanti se mi dà fastidio. Tranne se l'inizializzazione e la terminazione sono in realtà un problema importante nel tuo programma.

Eviterei le fabbriche in generale, finché non ne avrai un'esigenza specifica, che non è necessaria qui.

Is this a pattern that I'm not aware of (seems similar to Maven's context.xml)?

Un pattern non è un modo per implementare una soluzione, è solo un esempio di design. Non dare per scontato che si tratti di un modello diverso rispetto a Dependency Injection, in realtà è un altro modo di fare lo stesso.

For pure constructor injection, do I simply suffer the cost of "upfront" allocation?

Non sono sicuro di cosa intendi con questo: allocare tutto prima è un buon modo per evitare la frammentazione della memoria. Tuttavia, implica che si evitino quante più chiamate nuove / eliminate possibile, raggruppando le cose insieme se si desidera ottimizzare lo spazio e il tempo di esecuzione, quindi il mio suggerimento di raggruppare le dipendenze (quando sono necessarie solo nella chiamata di esecuzione) è azzeccato se ti interessa.

    
risposta data 28.11.2013 - 01:29
fonte

Leggi altre domande sui tag