Tensione tra il principio di inversione delle dipendenze ed evitare "nuovo" in C ++?

5

Ho visto molti consigli che è meglio fare Type object; di

Type* object = new Type();

in C ++ quando possibile-cioè, riduci al minimo l'utilizzo di new . Capisco il razionale dietro questo e lo apprezzo.

Ma secondo la mia comprensione, per fare pratica l'inversione di dipendenza richiede dei puntatori, per esempio:

Type* object = new Implementation();

dove Type è astratto (vale a dire contiene almeno un metodo virtuale puro) e Implementation è concreto. Non è possibile fare

Type object = Implementation();

perché ciò significa:

Type object;
object = Implementation();

che richiede inizialmente la creazione di object come Type , ma ciò non può essere fatto, poiché Type è astratto.

Esiste una tensione intrinseca tra il principio di inversione delle dipendenze ed evita new quando si usa C ++? In tal caso, quali modelli / principi / pratiche possono essere utilizzati per attenuare questa tensione?

    
posta Kazark 27.11.2012 - 21:43
fonte

3 risposte

7

Puoi assolutamente usare l'Iniezione delle Dipendenze senza usare mai new() . Il polimorfismo, che è quello di cui stai parlando qui, è realizzato in C ++ usando puntatori e / o riferimenti. Doc Brown ha già affrontato i riferimenti, parliamo dei puntatori.

Come esempio (supponendo che Implementation sia derivato da Type , e il costruttore di Bar prende un puntatore a Type ):

Implementation imp;
Bar bar(&imp);

L'operatore "indirizzo di" ( & ) prende l'indirizzo di un oggetto, il risultato è un puntatore all'oggetto. Questo è un modo diverso per ottenere un puntatore a un oggetto (diverso rispetto all'uso di new , ovvero.)

In C ++, ogni volta che ho un puntatore a Derived , posso anche usarlo come puntatore a Base . Questa è l'idea del polimorfismo, un Derived è un Base , giusto?

Se il mio esempio in due passaggi di cui sopra ha fatto un salto che non è stato possibile seguire, lo ripeterò qui, ma con un altro passaggio (non necessario):

Implementation imp;
Type *t = &imp;
Bar bar(t);

t è un puntatore a Type , quindi può puntare a qualsiasi classe derivata da Type , incluso Implementation . Nessun utilizzo di new . Nessuna tensione.

    
risposta data 28.11.2012 - 07:56
fonte
7

DIP significa invece di istanziare oggetti di classe Foo direttamente nella classe Bar , hai un'interfaccia astratta IFoo e passa oggetti già costruiti di tipo IFoo in Bar (ad esempio, attraverso il costruttore di Bar ). Ciò consente di sostituire facilmente Foo oggetti in Bar con MockFoo oggetti, ad esempio, a scopo di test. Se quegli oggetti sono costruiti in pila come

 Foo foo;
 Bar bar(&foo);

o dinamicamente come

 IFoo *foo = new Foo();
 Bar bar(foo);

dove Bar costruttore ha la firma

  Bar(IFoo *foo)
  {
       //...
  }

non ha importanza in termini di DIP, quella decisione dipende solo dalla durata prevista dell'oggetto foo (che in entrambi i casi dovrebbe essere almeno il tempo di vita di bar ).

La variante del puntatore è tecnicamente diversa su un aspetto: se lo desideri, puoi trasferire la proprietà dell'oggetto foo a bar e lasciare che bar esegua la pulizia della memoria chiamando delete su foo nel distruttore. Tuttavia, è discutibile se questo è un buon stile di programmazione e non ti consiglierei questa tecnica. Se desideri avere una pulizia automatica di foo quando la barra non ne ha più bisogno, ti suggerisco di utilizzare meglio i puntatori intelligenti.

Per la tua modifica: immagino che tu abbia un'idea sbagliata su puntatori e "nuovi". Questo codice

 Implementation object;
 Type *ptrObject = &object;

ti dà un puntatore a un oggetto di tipo Type senza usare new . O in breve:

"Avoiding the usage of new" != "You cannot have pointers"

(ed entrambi sono irrilevanti per il principio di inversione di dipendenza).

    
risposta data 27.11.2012 - 23:32
fonte
0

No, l'inversione di dipendenza non richiede puntatori.

La decisione di utilizzare i puntatori o meno è una questione di condivisione dei dati e ottimizzazione della memoria.

L'inversione di dipendenza ha a che fare con le gerarchie e l'ereditarietà delle classi.

Tutti gli stessi luoghi in cui prevedi di passare Type* che è stato allocato con new Type() possono essere modificati per accettare un Type& creato con type = Type() .

    
risposta data 27.11.2012 - 22:00
fonte

Leggi altre domande sui tag