Disons que j'ai une classe Foobarqui utilise (dépend) de la classe Widget. Au bon Widgetvieux temps, wolud serait déclaré comme un champ dans Foobar, ou peut-être comme un pointeur intelligent si un comportement polymorphe était nécessaire, et il serait initialisé dans le constructeur:
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)) {}
(…)
};
Et nous serions fin prêts. Malheureusement, aujourd'hui, les enfants Java se moqueront de nous une fois qu'ils le verront, et à juste titre, en couple Foobaret Widgetensemble. La solution est apparemment simple: appliquer l'injection de dépendances pour sortir la construction des dépendances de la Foobarclasse. Mais alors, C ++ nous oblige à penser à la propriété des dépendances. Trois solutions me viennent à l'esprit:
Pointeur unique
class Foobar {
std::unique_ptr<Widget> widget;
public:
Foobar(std::unique_ptr<Widget> &&w) : widget(w) {}
(…)
}
Foobarrevendique la propriété exclusive de ce Widgetqui lui est transmis. Cela présente les avantages suivants:
- L'impact sur les performances est négligeable.
- Il est sûr, car il
Foobarcontrôle la durée de vie de son appareilWidget, il s'assure donc qu'ilWidgetne disparaît pas soudainement. - C'est sûr qui
Widgetne fuira pas et sera correctement détruit lorsqu'il ne sera plus nécessaire.
Cependant, cela a un coût:
- Il impose des restrictions sur la façon dont les
Widgetinstances peuvent être utilisées, par exemple, aucune allocation de pile neWidgetspeut être utilisée, aucune neWidgetpeut être partagée.
Pointeur partagé
class Foobar {
std::shared_ptr<Widget> widget;
public:
Foobar(const std::shared_ptr<Widget> &w) : widget(w) {}
(…)
}
C'est probablement l'équivalent le plus proche de Java et d'autres langages récupérés. Avantages:
- Plus universel, car il permet de partager des dépendances.
- Maintient la sécurité (points 2 et 3) de la
unique_ptrsolution.
Désavantages:
- Gaspille des ressources quand aucun partage n'est impliqué.
- Nécessite toujours l'allocation de segments et interdit les objets alloués à la pile.
Pointeur d'observation ordinaire
class Foobar {
Widget *widget;
public:
Foobar(Widget *w) : widget(w) {}
(…)
}
Placez le pointeur brut dans la classe et transférez le fardeau de la propriété à quelqu'un d'autre. Avantages:
- Aussi simple que possible.
- Universel, accepte n'importe lequel
Widget.
Les inconvénients:
- Plus sûr.
- Présente une autre entité qui est responsable de la propriété des deux
FoobaretWidget.
Une métaprogrammation de modèle fou
Le seul avantage auquel je peux penser est que je pourrais lire tous ces livres pour lesquels je n'ai pas trouvé le temps pendant que mon logiciel est en train de se développer;)
Je penche vers la troisième solution, car elle est la plus universelle, quelque chose doit être géré de Foobarstoute façon, donc la gestion Widgetsest un simple changement. Cependant, l'utilisation de pointeurs bruts me dérange, d'un autre côté, la solution de pointeur intelligent me semble mal, car ils font que le consommateur de dépendance restreint la façon dont cette dépendance est créée.
Suis-je en train de manquer quelque chose? Ou est-ce que l'injection de dépendances en C ++ n'est pas anodine? La classe doit-elle posséder ses dépendances ou simplement les observer?
Foobarc'est le seul propriétaire? Dans l'ancien cas, c'est simple. Mais le problème avec DI, comme je le vois, est qu'il dissocie la classe de la construction de ses dépendances, il la dissocie également de la propriété de ces dépendances (car la propriété est liée à la construction). Dans les environnements récupérés comme Java, ce n'est pas un problème. En C ++, c'est.
std::unique_ptrc'est la voie à suivre. Vous pouvez utiliserstd::move()pour transférer la propriété d'une ressource de l'étendue supérieure à la classe.