J'ai cette ancienne implémentation du modèle de commande. C'est en quelque sorte passer un Contexte à travers toute l' implémentation de DIOperation , mais j'ai réalisé plus tard, dans le processus d'apprentissage et d'apprentissage (qui ne s'arrête jamais), que ce n'est pas optimal. Je pense aussi que la "visite" ici ne correspond pas vraiment et confond simplement.
Je pense en fait à refactoriser mon code, également parce qu'une commande ne devrait rien savoir des autres et, pour le moment, elles partagent toutes les mêmes paires clé-valeur. Il est vraiment difficile de conserver quelle classe possède quelle valeur-clé, ce qui conduit parfois à des variables en double.
Un exemple de cas d'utilisation: disons que CommandB nécessite UserName qui est défini par CommandA . CommandA doit-il définir la clé UserNameForCommandB = John ? Ou devraient-ils partager une valeur-clé UserName = John commune ? Que faire si le nom d'utilisateur est utilisé par une troisième commande?
Comment puis-je améliorer cette conception? Merci!
class DIParameters {
public:
/**
* Parameter setter.
*/
virtual void setParameter(std::string key, std::string value) = 0;
/**
* Parameter getter.
*/
virtual std::string getParameter(std::string key) const = 0;
virtual ~DIParameters() = 0;
};
class DIOperation {
public:
/**
* Visit before performing execution.
*/
virtual void visitBefore(DIParameters& visitee) = 0;
/**
* Perform.
*/
virtual int perform() = 0;
/**
* Visit after performing execution.
*/
virtual void visitAfter(DIParameters& visitee) = 0;
virtual ~DIOperation() = 0;
};
la source
Réponses:
Je m'inquiète un peu de la mutabilité de vos paramètres de commande. Est-il vraiment nécessaire de créer une commande avec des paramètres en constante évolution?
Problèmes avec votre approche:
Voulez-vous que d'autres threads / commandes modifient vos paramètres pendant le
perform
déroulement?Voulez - vous
visitBefore
etvisitAfter
du mêmeCommand
objet à être appelé avec différentsDIParameter
objets?Voulez-vous que quelqu'un fournisse des paramètres à vos commandes, dont les commandes n'ont aucune idée?
Rien de tout cela n'est interdit par votre conception actuelle. Bien qu'un concept générique de paramètre clé-valeur ait parfois ses mérites, je ne l'aime pas par rapport à une classe de commande générique.
Exemple de conséquences:
Considérez une réalisation concrète de votre
Command
classe - quelque chose commeCreateUserCommand
. Maintenant, évidemment, lorsque vous demandez la création d'un nouvel utilisateur, la commande aura besoin d'un nom pour cet utilisateur. Étant donné que je connais lesCreateUserCommand
et lesDIParameters
classes, paramètres dois - je régler?Je pourrais régler le
userName
paramètre, ouusername
.. traitez-vous les paramètres de manière insensible? Je ne sais pas vraiment ... oh attends ... c'est peut-être justename
?Comme vous pouvez le voir, la liberté que vous gagnez d'un mappage de valeur-clé générique implique que l'utilisation de vos classes en tant que personne qui ne les a pas implémentées est indûment difficile. Vous auriez au moins besoin de fournir certaines constantes pour vos commandes afin que les autres sachent quelles clés sont prises en charge par cette commande.
Différentes approches de conception possibles:
Parameter
instances immuables, vous pouvez les réutiliser librement parmi différentes commandes.UserParameter
classe qui contient exactement les paramètres dont j'avais besoin pour les commandes impliquant un utilisateur, il serait beaucoup plus simple de travailler avec cette API. Vous pouvez toujours avoir un héritage sur les paramètres, mais il ne serait plus logique que les classes de commandes prennent des paramètres arbitraires - côté pro, cela signifie bien sûr que les utilisateurs de l'API savent exactement quels paramètres sont requis.visitBefore
etvisitAfter
, tout en les réutilisant avec différents paramètres, vous serez ouvert au problème d'être appelé avec des paramètres différents. Si les paramètres doivent être identiques sur plusieurs appels de méthode, vous devez les encapsuler dans la commande de sorte qu'ils ne puissent pas être désactivés pour d'autres paramètres entre les appels.la source
Ce qui est bien avec les principes de conception, c'est que tôt ou tard, ils entrent en conflit les uns avec les autres.
Dans la situation décrite, je pense que je préférerais aller avec une sorte de contexte dans lequel chaque commande peut prendre des informations et les mettre (en particulier s'il s'agit de paires clé-valeur). Ceci est basé sur un compromis: je ne veux pas que des commandes séparées soient couplées simplement parce qu'elles sont une sorte d'entrée les unes aux autres. À l'intérieur de CommandB, je me fiche de la façon dont UserName a été défini - juste qu'il est là pour moi. Même chose dans CommandA: j'ai mis les informations dedans, je ne veux pas savoir ce que les autres en font - ni qui ils sont.
Cela signifie une sorte de contexte passager, que vous pouvez trouver mauvais. Pour moi, l'alternative est pire, surtout si ce contexte clé-valeur simple (peut être un simple bean avec des getters et setters, pour limiter un peu le facteur "forme libre") peut permettre à la solution d'être simple et testable, avec bien commandes séparées, chacune avec sa propre logique métier.
la source
Supposons que vous ayez une interface de commande:
Et un sujet:
Ce dont vous avez besoin c'est:
Définir
NameObserver& o
comme référence à CommandB. Désormais, chaque fois que CommandA change le nom des sujets, CommandB peut s'exécuter avec les informations correctes. Si le nom est utilisé par plusieurs commandes, utilisez unstd::list<NameObserver>
la source
Je ne sais pas si c'est la bonne façon de gérer cela sur les programmeurs (auquel cas je m'excuse), mais après avoir vérifié toutes les réponses ici (@ Frank's en particulier). J'ai refactorisé mon code de cette façon:
Merci pour l'aide et j'espère que je n'ai pas fait d'erreur ici :)
la source