Les deux modèles de conception encapsulent un algorithme et découplent les détails d'implémentation de leurs classes d'appel. La seule différence que je peux discerner est que le modèle Stratégie prend en compte les paramètres d'exécution, contrairement au modèle Commande.
Il me semble que le modèle de commande nécessite que toutes les informations d'exécution soient disponibles lors de sa création et qu'il soit en mesure de différer son appel (éventuellement dans le cadre d'un script).
Quelles déterminations guident s'il faut utiliser l'un ou l'autre motif?
J'inclus une table de hiérarchie d'encapsulation de plusieurs modèles de conception GoF pour aider à expliquer les différences entre ces deux modèles. J'espère que cela illustre mieux ce que chacune d'elles encapsule pour que mon explication ait plus de sens.
Tout d'abord, la hiérarchie répertorie la portée à laquelle un modèle donné est applicable ou le modèle approprié à utiliser pour encapsuler un niveau de détail, en fonction du côté de la table où vous commencez.
Comme vous pouvez le voir dans le tableau, un objet Modèle de stratégie cache les détails de la mise en œuvre d'un algorithme. L'utilisation d'un objet de stratégie différent permet donc d'obtenir les mêmes fonctionnalités, mais de manière différente. Chaque objet de stratégie peut être optimisé pour un facteur particulier ou agir sur un autre paramètre. et, grâce à l'utilisation d'une interface commune, le contexte peut fonctionner en toute sécurité.
Le modèle de commande encapsule un niveau de détail bien inférieur à celui d'un algorithme. Il code 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 invoqués de manière générale à différents moments ou locus 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 qu'il soit nécessaire de connaître les détails d'un appel spécifique avant leur exécution.
Comme c'est le cas 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 et comme arguments de méthode.
Les stratégies encapsulent des algorithmes. Les commandes séparent l'expéditeur du destinataire d'une demande, elles transforment une demande en objet.
Si c'est un algorithme, comment faire quelque chose, utilisez une stratégie. Si vous devez 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.
Répondre à une très vieille question. (Quelqu'un voit-il les dernières réponses au lieu des plus votées?)
C'est une confusion valable d'avoir à cause des similitudes. Les modèles de stratégie et de commande utilisent encapsulation. Mais cela ne les rend pas identiques.
La principale différence est de comprendre ce que est encapsulé. Le principe OO, dont dépendent les deux modèles, est Encapsulate ce qui varie.
En cas de stratégie, ce qui varie est algorithm. Par exemple, un objet de stratégie sait comment exporter dans un fichier XML, tandis que l'autre affiche, par exemple, JSON. Différents algorithmes sont conservés (encapsulated) dans différentes classes. C'est aussi simple que ça.
En cas de commande, ce qui varie est la request elle-même. La demande peut provenir de File Menu > Delete
ou Right Click > Context Menu > Delete
ou 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. Comme les demandes sont maintenant un tas d’objets, nous pourrions les gérer facilement. Soudain, il devient trivial de fournir des fonctionnalités telles que annuler ou rétablir.
Peu importe la manière dont la commande implémente la logique demandée. Lors de l'appel de execute (), il peut implémenter un algorithme pour déclencher la suppression ou même le déléguer à d'autres objets, voire déléguer à une stratégie. Ce ne sont que des détails d'implémentation du modèle de commande. C'est pourquoi il s'appelle command bien que ce ne soit pas un moyen poli de request: -)
Contraste avec la stratégie; ce modèle ne concerne que le logic réel qui est exécuté. Si nous le faisons, cela aide d’obtenir différentes combinaisons de comportements avec un ensemble minimal de classes, empêchant ainsi l’explosion de la classe.
Je pense que Command nous aide à élargir notre compréhension de l’encapsulation alors que Strategy fournit une utilisation naturelle de l’encapsulation et du polymorphisme.
La façon dont je vois les choses, c’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 doit être 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 devrait essayer de traverser la pièce, mais à l'intérieur de celui-ci, il pourrait essayer StrategyOne et StrategyTwo pour avoir tenté de traverser la pièce.
Marque
Je me trompe peut-être, mais je considère la commande comme une fonction à exécuter ou une réaction. Il devrait y avoir au moins deux joueurs: celui qui demande l'action et celui qui l'exécute. L'interface graphique est un exemple typique de modèle de commande:
La commande est généralement liée à une étendue ou à un domaine d'activité, mais n'est pas nécessaire: vous pouvez avoir des commandes qui émettent une facture, lancent une fusée ou suppriment un fichier implémentant la même interface (par exemple, la méthode single execute()
) au sein d'une application. Les commandes sont souvent autonomes, elles n'ont donc besoin de rien de la part de l'exécuteur pour traiter la tâche à laquelle elles sont destinées (toutes les informations nécessaires sont fournies au moment de la construction), parfois les commandes sont sensibles au contexte et doivent pouvoir le découvrir. (La commande Backspace doit connaître la position du curseur dans le texte pour supprimer correctement le caractère précédent; la commande Rollback doit détecter la transaction en cours à annuler; ...).
La stratégie est un peu différente: elle est plus liée à une zone. La stratégie peut définir une règle pour formater une date (au format UTC? Spécifique à la localisation?) (Stratégie de "formatage de date") ou pour calculer un carré pour une figure géométrique (stratégie de "calculateur carré"). Les stratégies sont en ce sens des objets flyweight, qui prennent quelque chose en entrée ("date", "chiffre", ...) et prennent une décision sur leur base. Le meilleur exemple de stratégie est peut-être pas celui qui est lié à l'interface javax.xml.transform.Source
: selon que l'objet transmis est DOMSource
ou SAXSource
ou StreamSource
, la stratégie (= transformateur XSLT dans ce cas) appliquera des règles différentes pour le traiter. L'implémentation peut être une simple switch
ou impliquer modèle de chaîne de responsabilité .
Mais en réalité, il existe un point commun entre ces deux modèles: les commandes et les stratégies encapsulent des algorithmes dans la même zone sémantique.
Commande:
Composants de base:
execute()
Flux de travail:
Client appelle Invoker => Invoker appelle ConcreteCommand => ConcreteCommand appelle la méthode Receiver, qui implémente la méthode abstraite Command.
Advantage: le client n'est pas affecté par les modifications apportées à Command and Receiver. Invoker fournit un couplage lâche entre le client et le récepteur. Vous pouvez exécuter plusieurs commandes avec le même Invoker.
Le motif Command vous permet d'exécuter une commande sur différents récepteurs en utilisant le même invocateur. L'invocateur ignore le type de récepteur
Pour une meilleure compréhension des concepts, consultez le JournalDev article de Pankaj Kumar et dzone article de James Sugrue en plus du lien Wikipedia.
Vous pouvez utiliser le motif Command pour
Découpler l'invocateur et le destinataire de la commande
Implémenter un mécanisme de rappel
Implémenter les fonctionnalités d'annulation et de restauration
Maintenir un historique des commandes
Java.lang.Thread
est une bonne implémentation du modèle Command. Vous pouvez traiter Thread comme invocateur et classe implémentant Runnable comme ConcreteCommonad/Receiver et la méthode run()
comme Commande.
La version annulée/rétablie du modèle de commande peut être lue à l'adresse Theodore Norvellarticle
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 d'algorithme peut changer au moment de l'exécution en fonction de conditions particulières.
Prenons un exemple de composant de tarif du système de réservation du transport aérien
Les compagnies aériennes souhaiteraient proposer des tarifs différents selon les périodes - mois de pointe et mois creuses. En dehors des heures de pointe, il souhaite stimuler la demande en proposant des réductions attractives.
Points clés du modèle Strategy:
Articles connexes avec des exemples de code:
Pour moi, la différence est une 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 (il l'utilisera pour effectuer une partie de son propre travail), mais il s'en fiche comment il le fait.
Pour une commande, le composant utilisant l'objet ne sait ni que la commande ni comment il le fait - il sait juste 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 principal de l'appelant.
C'est la différence - l'objet utilisant le composant sait-il réellement ce que fait le composant ou s'en soucie-t-il? La plupart du temps, cela peut être déterminé en fonction du fait que l'objet de modèle retourne une valeur à son appelant. Si l'invocateur se soucie de ce que fait l'objet modèle, il voudra probablement qu'il retourne quelque chose et ce sera une stratégie. S'il ne se soucie pas de n'importe quelle valeur de retour, il s'agit probablement d'une commande (remarque, quelque chose comme un Java Callable est toujours une commande car, bien qu'elle renvoie une valeur, l'appelant ne se soucie pas de la valeur, il la renvoie à tout ce qui a fourni la commande à l’origine).