Disons que j'ai une classe Foobar
qui utilise (dépend) de la classe Widget
. Au bon Widget
vieux 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 Foobar
et Widget
ensemble. La solution est apparemment simple: appliquer l'injection de dépendances pour sortir la construction des dépendances de la Foobar
classe. 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) {}
(…)
}
Foobar
revendique la propriété exclusive de ce Widget
qui lui est transmis. Cela présente les avantages suivants:
- L'impact sur les performances est négligeable.
- Il est sûr, car il
Foobar
contrôle la durée de vie de son appareilWidget
, il s'assure donc qu'ilWidget
ne disparaît pas soudainement. - C'est sûr qui
Widget
ne 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
Widget
instances peuvent être utilisées, par exemple, aucune allocation de pile neWidgets
peut être utilisée, aucune neWidget
peut ê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_ptr
solution.
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
Foobar
etWidget
.
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 Foobars
toute façon, donc la gestion Widgets
est 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?
Foobar
c'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_ptr
c'est la voie à suivre. Vous pouvez utiliserstd::move()
pour transférer la propriété d'une ressource de l'étendue supérieure à la classe.