Utilisation d'un modèle de stratégie et d'un modèle de commande

121

Les deux modèles de conception encapsulent un algorithme et découplent les détails d'implémentation de leurs classes appelantes. La seule différence que je peux discerner est que le modèle de stratégie prend des paramètres pour l'exécution, contrairement au modèle de commande.

Il me semble que le modèle de commande nécessite que toutes les informations pour l'exécution soient disponibles lors de sa création, et il est capable de retarder son appel (peut-être dans le cadre d'un script).

Quelles déterminations guident s'il faut utiliser un modèle ou l'autre?

Extrakun
la source

Réponses:

94

J'inclus un tableau de hiérarchie d'encapsulation de plusieurs des modèles de conception du GoF pour aider à expliquer les différences entre ces deux modèles. J'espère que cela illustre mieux ce que chacun résume afin que mon explication ait plus de sens.

Tout d'abord, la hiérarchie répertorie la portée pour laquelle un modèle donné est applicable, ou le modèle approprié à utiliser pour encapsuler un certain niveau de détail, selon le côté du tableau par lequel vous commencez.

table de hiérarchie d'encapsulation de modèle de conception

Comme vous pouvez le voir dans le tableau, un objet de modèle de stratégie masque les détails de l'implémentation d'un algorithme, de sorte que l'utilisation d'un objet de stratégie différent exécutera la même fonctionnalité mais d'une manière différente. Chaque objet de stratégie peut être optimisé pour un facteur particulier ou fonctionner sur un autre paramètre; et, grâce à l'utilisation d'une interface commune, le contexte peut fonctionner en toute sécurité avec l'un ou l'autre.

Le modèle de commande encapsule un niveau de détail beaucoup plus petit qu'un algorithme. Il encode les détails nécessaires pour envoyer un message à un objet: récepteur, sélecteur et arguments. L'avantage d'objectiver une si petite partie de l'exécution du processus est que de tels messages peuvent être appelés à différents moments ou emplacements de manière générale sans avoir à coder en dur ses détails. Il permet aux messages d'être invoqués une ou plusieurs fois, ou transmis à différentes parties du système ou à plusieurs systèmes sans exiger que les détails d'un appel spécifique soient connus avant l'exécution.

Comme c'est typique pour les modèles de conception, ils ne nécessitent pas que toutes les implémentations soient identiques en détail pour porter le nom du modèle. Les détails peuvent varier dans l'implémentation et dans quelles données sont encodées dans l'objet par rapport aux arguments de méthode.

Huperniketes
la source
Donc, si j'avais un système qui filtrait les résultats avec un «pipeline de filtres» et utilisait des délégués comme filtres (où chacun des algorithmes du filtre serait encapsulé dans une fonction), cela serait-il considéré comme un modèle de commande? Dans ce cas, je vois le délégué pour la fonction de filtre comme fournissant une sorte de contrat pour ce à quoi chaque filtre doit adhérer en termes d'entrée et de sortie.
KTF
4
@KTF, non. Le modèle de commande utilise un objet qui a la plupart (sinon la totalité) des informations nécessaires (par exemple, le récepteur, le sélecteur, les arguments) pour invoquer la méthode d'un objet. C'est un modèle simpliste qui peut être utilisé dans d'autres modèles de conception tels que la chaîne de responsabilité, la collection et le modèle de pipeline que vous décrivez. Le "contrat de toutes sortes" fourni par vos délégués est un autre modèle, Interface.
Huperniketes
50

Les stratégies encapsulent des algorithmes. Les commandes séparent l'expéditeur du destinataire d'une requête, elles transforment une requête en objet.

Si c'est un algorithme, comment quelque chose sera fait, utilisez une stratégie. Si vous avez besoin de séparer l'appel d'une méthode de son exécution, utilisez une commande. Les commandes sont souvent utilisées lorsque vous mettez en file d'attente des messages pour une utilisation ultérieure, comme une tâche ou une transaction.

Paul Rubel
la source
cela avait du sens en.wikipedia.org/wiki/Command_Pattern client et invocateur sont liés, mais en même temps, ils ne se connaissent pas!
Kalpesh Soni
26

Répondre à une question très ancienne. (Est-ce que quelqu'un voit les dernières réponses au lieu des plus votées?)

C'est une confusion valable à avoir en raison des similitudes. Les modèles de stratégie et de commande utilisent l' encapsulation . Mais cela ne les rend pas pareils.

La principale différence est de comprendre ce qui est encapsulé. Le principe OO, dont dépendent les deux modèles, est d' encapsuler ce qui varie .

En cas de stratégie, ce qui varie, c'est l' algorithme . Par exemple, un objet de stratégie sait comment générer une sortie dans un fichier XML, tandis que l'autre sort vers, par exemple, JSON. Différents algorithmes sont conservés ( encapsulés ) dans différentes classes. C'est aussi simple que ça.

En cas de commande, ce qui varie, c'est la requête elle-même. La demande peut provenir de File Menu > Deleteou Right Click > Context Menu > Deleteou Just Delete Button pressed. Les trois cas peuvent générer 3 objets de commande du même type. Ces objets de commande ne représentent que 3 demandes de suppression; pas d'algorithme de suppression. Puisque les demandes sont maintenant un tas d'objets, nous pourrions les gérer facilement. Tout à coup, il devient trivial de fournir des fonctionnalités telles que l'annulation ou la restauration.

La manière dont la commande implémente la logique demandée n'a pas d'importance. En appelant execute (), il peut implémenter un algorithme pour déclencher la suppression ou il peut même le déléguer à d'autres objets, voire déléguer à une stratégie. Il ne s'agit que du détail d'implémentation du modèle de commande. C'est pourquoi il est nommé comme commande bien que ce ne soit pas une manière polie de demander : -)

Mettez-le en contraste avec la stratégie; ce modèle ne concerne que la logique réelle qui est exécutée. Si nous faisons cela, cela aide à obtenir différentes combinaisons de comportements avec un ensemble minimal de classes, empêchant ainsi l'explosion de classe.

Je pense que Command nous aide à élargir notre compréhension de l'encapsulation tandis que Strategy fournit une utilisation naturelle de l'encapsulation et du polymorphisme.

rpattabi
la source
15

La façon dont je vois les choses est que vous avez plusieurs façons de faire la même chose, chacune d'elles est une stratégie, et quelque chose au moment de l'exécution détermine quelle stratégie sera exécutée.

Essayez peut-être d'abord StrategyOne, si les résultats ne sont pas assez bons, essayez StrategyTwo ...

Les commandes sont liées à des choses distinctes qui doivent se produire, comme TryToWalkAcrossTheRoomCommand. Cette commande sera déclenchée chaque fois qu'un objet essaiera de traverser la pièce, mais à l'intérieur, il pourrait essayer StrategyOne et StrategyTwo pour essayer de traverser la pièce.

marque

MStodd
la source
2
RE: «plusieurs façons de faire la même chose» - Cela semble entrer en conflit avec certains des exemples courants de stratégie. Plus précisément, celles où il y a des classes d'implémentation qui font l'addition, la soustraction, la multiplication, etc. Ce ne sont peut-être pas de bons exemples?
Joshua Davis
1
@JoshuaDavis tous ces "substrats" sont des cas particuliers d'une stratégie: l'opération arithmétique . ils ont des arguments communs (2 opérandes) et produisent une valeur comme résultat. à peu près faire la même chose (être des boîtes noires) à leur manière, selon l'implémentation. donc je ne vois pas de conflit ici, mais, bien au contraire: bel exemple =)
jungle_mole
7

Je peux me tromper à mon avis, mais je considère la commande comme une fonction à exécuter ou une réaction. Il doit y avoir au moins deux joueurs: celui qui demande l'action et celui qui exécute l'action. L'interface graphique est un exemple typique de modèle de commande:

  • Tous les boutons de la barre d'outils de l'application sont associés à une action.
  • Button est l'exécuteur testamentaire dans ce cas.
  • L'action est la commande dans ce cas.

La commande est généralement limitée à une certaine portée ou domaine d'activité, mais pas nécessaire: vous pouvez avoir des commandes qui émettent une facture, démarrer une fusée ou supprimer un fichier implémentant la même interface (par exemple, une seule execute()méthode) dans une seule application. Souvent, les commandes sont autonomes, elles n'ont donc besoin de rien de l'exécuteur pour traiter la tâche à laquelle elles sont destinées (toutes les informations nécessaires sont données au moment de la construction), parfois les commandes sont contextuelles et devraient être en mesure de découvrir ce contexte (La commande de retour arrière doit connaître la position du curseur dans le texte pour supprimer correctement le caractère précédent; Retour en arrière commande doit découvrir la transaction en cours de restauration, ...).

La stratégie est un peu différente: elle est plus liée à un domaine. La stratégie peut définir une règle pour formater une date (en UTC? Locale spécifique?) (Stratégie de "formatage de date") ou pour calculer un carré pour une figure géométrique (stratégie "calculatrice carrée"). Les stratégies sont en ce sens des objets poids mouche, qui prennent quelque chose comme entrée ("date", "figure", ...) et prennent une décision sur sa base. Peut-être pas le meilleur, mais un bon exemple de stratégie est celui lié à l' javax.xml.transform.Sourceinterface: selon que l'objet passé est DOMSourceou SAXSourceou StreamSourcela stratégie (= transformateur XSLT dans ce cas) appliquera des règles différentes pour le traiter. La mise en œuvre peut être un modèle de chaîne de responsabilité simple switchou impliquer .

Mais en effet, il y a quelque chose en commun entre ces deux modèles: les commandes et les stratégies encapsulent des algorithmes dans le même domaine sémantique.

dma_k
la source
1
Je traite la commande comme une fonction de rappel ou une réaction. Il devrait y avoir au moins deux joueurs: l'un qui demande l'action, et l'autre qui exécute ... - Je comprends ce que vous essayez de dire, mais j'hésiterais à utiliser le mot `` rappel '', car souvent le mot 'callback' implique un appel asynchrone et vous n'avez pas besoin de faire des appels asynchrones pour que le modèle de commande soit utile. Exemple concret: Microsoft Word. Les clics sur les boutons de la barre d'outils et les touches de raccourci n'appellent pas de commandes asynchrones, mais nous pouvons comprendre comment le modèle de commande serait utile dans ce cas
Jim G.
Je suis d'accord, bien que, comme Jim l'a dit, je modifierais pour supprimer la référence au rappel.
JARC
Merci, j'ai fait quelques extensions. Faites-moi savoir, si vous êtes d'accord / pas d'accord.
dma_k
5

Commander:

Composants de base:

  1. La commande déclare une interface pour les commandes abstraites commeexecute()
  2. Le récepteur sait comment exécuter une commande particulière
  3. Invoker détient ConcreteCommand , qui doit être exécuté
  4. Le client crée ConcreteCommand et affecte le récepteur
  5. ConcreteCommand définit la liaison entre Command et Receiver

Flux de travail:

Le client appelle Invoker => Invoker appelle ConcreteCommand => ConcreteCommand appelle la méthode Receiver , qui implémente la méthode de commande abstraite .

Avantage : le client n'est pas affecté par les changements dans la commande et le récepteur. L'invocateur fournit un couplage lâche entre le client et le récepteur. Vous pouvez exécuter plusieurs commandes avec le même invocateur.

Le modèle de commande vous permet d'exécuter une commande sur différents récepteurs en utilisant le même invocateur . L'invocateur ne connaît pas le type de récepteur

Pour une meilleure compréhension des concepts, jetez un œil à cet article JournalDev de Pankaj Kumar et à l' article dzone de James Sugrue en plus du lien Wikipedia.

Vous pouvez utiliser le modèle de commande pour

  1. Découpler l'invocateur et le récepteur de commande

  2. Mettre en œuvre un mécanisme de rappel

  3. Implémenter la fonctionnalité d'annulation et de rétablissement

  4. Maintenir un historique des commandes

java.lang.Threadest une bonne implémentation du modèle de commande . Vous pouvez traiter Thread comme invocateur et la classe implémentant Runnable comme ConcreteCommonad / Receiver et la run()méthode comme Command .

La version Annuler / Rétablir du modèle de commande peut être lue dans l' article de Theodore Norvell

Stratégie:

Le modèle de stratégie est très simple à comprendre. Utilisez ce modèle lorsque

Vous avez plusieurs implémentations pour un algorithme et l'implémentation de l'algorithme peut changer au moment de l'exécution en fonction de conditions particulières .

Prenons un exemple du composant Fare du système de réservation de la compagnie aérienne

Les compagnies aériennes aimeraient offrir différents tarifs pendant différentes périodes - mois de pointe et hors pointe. Pendant les jours hors pointe, elle souhaite stimuler la demande en offrant des rabais intéressants.

Principaux points à retenir du modèle de stratégie :

  1. C'est un modèle de comportement
  2. C'est basé sur la délégation
  3. Cela change les tripes de l'objet en modifiant le comportement de la méthode
  4. Il est utilisé pour basculer entre les familles d'algorithmes
  5. Il modifie le comportement de l'objet au moment de l'exécution

Articles connexes avec des exemples de code:

Utilisation du modèle de conception de commande

Exemple du monde réel du modèle de stratégie

Ravindra babu
la source
0

Pour moi, la différence est une question d'intention. Les implémentations des deux modèles sont assez similaires, mais ont des objectifs différents:

  • Pour une stratégie, le composant utilisant l'objet sait ce que fait l'objet (et l'utilisera pour effectuer une partie de son propre travail), mais peu importe comment il le fait.

  • Pour une commande, le composant utilisant l'objet ne sait ni ce que fait la commande ni comment elle le fait - il sait simplement comment l'invoquer. La tâche de l'appelant consiste simplement à exécuter la commande - le traitement effectué par la commande ne fait pas partie du travail de base de l'appelant.

C'est la différence - est-ce que l'objet utilisant le composant sait ou se soucie réellement de ce que fait le composant? La plupart du temps, cela peut être déterminé en fonction du fait que l'objet modèle renvoie une valeur à son invocateur. Si l'invocateur se soucie de ce que fait l'objet modèle, il voudra probablement qu'il renvoie quelque chose et ce sera une stratégie. S'il ne se soucie d'aucune valeur de retour, il s'agit probablement d'une commande (notez que quelque chose comme un appelable Java est toujours une commande car, bien qu'il retourne une valeur, l'appelant ne se soucie pas de la valeur - il la renvoie simplement à tout ce qui a initialement fourni la commande).

BarrySW19
la source