Dire che ho una classe Foobar
che utilizza (dipende dalla) classe Widget
. Nei buoni vecchi giorni, Widget
wolud essere dichiarato come campo in Foobar
, o forse come puntatore intelligente se fosse necessario un comportamento polimorfico, e sarebbe inizializzato nel costruttore:
class Foobar {
Widget widget;
public:
Foobar() : widget(blah blah blah) {}
// or
std::unique_ptr<Widget> widget;
public:
Foobar() : widget(std::make_unique<Widget>(blah blah blah)) {}
(…)
};
E saremmo tutti pronti. Sfortunatamente, oggi, i bambini di Java ridono di noi una volta che li vedono, e giustamente, poiché accoppia Foobar
e Widget
insieme. La soluzione è apparentemente semplice: applica Dependency Injection per prendere la costruzione delle dipendenze dalla classe Foobar
. Ma poi, il C ++ ci costringe a pensare alla proprietà delle dipendenze. Vengono in mente tre soluzioni:
Puntatore univoco
class Foobar {
std::unique_ptr<Widget> widget;
public:
Foobar(std::unique_ptr<Widget> &&w) : widget(w) {}
(…)
}
Foobar
rivendica la proprietà esclusiva di Widget
che gli viene passata. Questo ha i seguenti vantaggi:
- L'impatto sulle prestazioni è trascurabile.
- È sicuro, dato che
Foobar
controlla la durata del suoWidget
, quindi si assicura cheWidget
non scompaia improvvisamente. - È sicuro che
Widget
non perderà e verrà distrutto correttamente quando non sarà più necessario.
Tuttavia, ciò ha un costo:
- pone restrizioni su come utilizzare le istanze
Widget
, ad es. non è possibile utilizzareWidgets
allocata allo stack, non è possibile condividereWidget
.
Puntatore condiviso
class Foobar {
std::shared_ptr<Widget> widget;
public:
Foobar(const std::shared_ptr<Widget> &w) : widget(w) {}
(…)
}
Questo è probabilmente l'equivalente più vicino di Java e di altri linguaggi raccolti da garbage collection. Vantaggi:
- Più universale, in quanto consente di condividere le dipendenze.
- Mantiene la sicurezza (punti 2 e 3) della soluzione
unique_ptr
.
Svantaggi:
- Spreca risorse quando non è coinvolta alcuna condivisione.
- Richiede ancora l'allocazione dell'heap e non consente gli oggetti allocati nello stack.
Semplice ol 'puntatore osservatore
class Foobar {
Widget *widget;
public:
Foobar(Widget *w) : widget(w) {}
(…)
}
Posiziona il puntatore raw all'interno della classe e sposta l'onere della proprietà su qualcun altro. Pro:
- Il più semplice possibile.
- Universale, accetta solo qualsiasi
Widget
.
Contro:
- Non è più sicuro.
- Presenta un'altra entità responsabile della proprietà di
Foobar
eWidget
.
Alcuni metaprogrammi del modello pazzo
L'unico vantaggio che posso pensare è che sarei in grado di leggere tutti quei libri per i quali non ho trovato il tempo mentre il mio software è in fase di produzione;)
Mi chiamo verso la terza soluzione, poiché è la più universale, qualcosa deve comunque gestire Foobars
, quindi gestire Widgets
è una semplice modifica. Tuttavia, l'uso di puntatori grezzi mi infastidisce, d'altra parte la soluzione di puntatore intelligente mi sembra sbagliata, poiché fanno in modo che i consumatori di dipendenza limitino il modo in cui viene creata tale dipendenza.
Mi manca qualcosa? O è solo la dipendenza dall'iniezione in C ++ non banale? La classe dovrebbe possedere le sue dipendenze o semplicemente osservarle?