Les indicateurs sécurisés doivent-ils être utilisés dans le modèle de stratégie?

Étant donné un modèle de stratégie typique

class Strategy { public: virtual int execute() const = 0; } class StrategyA : public Strategy { public: int execute() const override; } class StrategyB : public Strategy { public: int execute() const override; } 

Je crois que la façon de “mettre en place une classe de contexte” de “pré-C ++ 11” serait quelque chose comme

 class ContextRaw { public: ContextRaw(Strategy* the_strategy); ~ContextRaw(); // Should this delete the_strategy_? int execute() const; private: Strategy* the_strategy_; } 

Pour moi, dans cette conception, il n’est pas clair si Context doit assumer la responsabilité de la Strategy , et à moins d’une documentation claire indiquant le contraire, de mauvaises choses peuvent se produire si cela se produit.

 void trouble() { StrategyA a_concrete_strategy; ContextRaw a_context(&a_concrete_strategy); // Oops, Context may try to delete stack variable } void more_trouble() { Strategy* a_concrete_strategy = new StrategyA; ContextRaw* a_context = new ContextRaw(a_concrete_strategy); ContextRaw* another_context = new ContextRaw(a_concrete_strategy); delete a_context; std::cout << another_context.execute() << std::endl; // Oops, the_strategy is deleted } 

À la lumière des indicateurs de sécurité, devrait-il maintenant être préférable d’injecter un indicateur sûr et de laisser le Context s’approprier la Strategy ?

 class ContextUnique { public: ContextUnique() = delete; ContextUnique(std::unique_ptr the_strategy); ~ContextUnique(); int execute() const; private: std::unique_ptr the_strategy_; } 

ou si la Strategy peut être partagée entre différents Context ?

 class ContextShared { public: ContextShared() = delete; ContextShared(std::shared_ptr the_strategy); ~ContextShared(); int execute() const; private: std::shared_ptr the_strategy_; } 

Cette conception pose bien sûr des problèmes qui lui sont propres, en particulier seules les Strategy allouées dynamicment peuvent être injectées dans le Context .

Vous le faites mal.

À la lumière de std::function , tout ce que vous venez d’écrire est complètement obsolète et vous devriez simplement utiliser std::function et quelques lambdas.

La conception est à la mise en œuvre.

Notez que dans vos exemples, vous faites référence à différentes manières de visser avec un modèle de stratégie utilisant des pointeurs non C ++ 11.

Pour répondre directement à votre question:

Oui, vous devez utiliser des pointeurs intelligents dans le modèle de stratégie.

Pour répondre davantage à la question:

Vous devez utiliser des pointeurs intelligents autant que possible.

La raison en est que les pointeurs intelligents sont pratiquement auto-documentés en termes de politique de propriété de la mémoire, vous évacuant ainsi certains des inconvénients de “Si aucune bonne documentation”.

En considérant le prototype que vous exposez pour votre classe de contexte, vous pouvez indiquer aux utilisateurs quelles sont vos attentes:

  • unique_ptr si vous attendez de l’utilisateur qu’il vous transmette la propriété de la mémoire
  • shared_ptr si vous vous attendez à ce que la même implémentation de stratégie soit utilisée sur plusieurs propriétaires
  • faible_ptr si vous voulez que l’utilisateur gère la gestion de la mémoire

Ce qui est plus sûr, c’est à vous de décider. Cependant, vous pouvez dire aux utilisateurs que le contexte peut partager sa stratégie concrète avec d’autres contextes OU qu’il existe une stratégie concrète par contexte.

En tant qu’approche de conception, je suggèrerais de choisir 1 Stratégie / Contexte (donc unique_ptr), car vos stratégies concrètes pourraient éventuellement avoir des variables internes uniques / un contexte, et les choses deviendraient compliquées.

Cela dépend fortement du but réel des objects de stratégie si ceux-ci doivent être partagés entre divers objects de contexte ou possédés par eux.

Au moins, lorsque vous utilisez le ptr partagé ou unique, vous définissez clairement vos intentions. Vous ne devez utiliser un pointeur “brut” que si vous souhaitez “afficher” d’autres objects (vous ne les partagez pas et ne les possédez pas – et vous êtes sûr que l’object pointé ne survivra pas à l’object pointé).