Le modèle de stratégie fonctionne bien pour éviter les énormes constructions if ... else et faciliter l'ajout ou le remplacement de fonctionnalités. Cependant, cela laisse toujours un défaut à mon avis. Il semble que dans chaque implémentation, il doit encore y avoir une construction de branchement. Il peut s'agir d'une usine ou d'un fichier de données. Prenons par exemple un système de commande.
Usine:
// All of these classes implement OrderStrategy
switch (orderType) {
case NEW_ORDER: return new NewOrder();
case CANCELLATION: return new Cancellation();
case RETURN: return new Return();
}
Le code après cela n'a pas à s'inquiéter, et il n'y a qu'un seul endroit pour ajouter un nouveau type de commande maintenant, mais cette section de code n'est toujours pas extensible. Le retirer dans un fichier de données améliore la lisibilité (discutable, je sais):
<strategies>
<order type="NEW_ORDER">com.company.NewOrder</order>
<order type="CANCELLATION">com.company.Cancellation</order>
<order type="RETURN">com.company.Return</order>
</strategies>
Mais cela ajoute encore du code standard pour traiter le fichier de données - accordé, plus facilement testable unitaire et code relativement stable, mais une complexité supplémentaire néanmoins.
De plus, ce type de construction ne teste pas bien l'intégration. Chaque stratégie individuelle peut être plus facile à tester maintenant, mais chaque nouvelle stratégie que vous ajoutez est une complexité supplémentaire à tester. C'est moins que ce que vous auriez si vous n'aviez pas utilisé le modèle, mais il est toujours là.
Existe-t-il un moyen de mettre en œuvre le modèle de stratégie qui atténue cette complexité? Ou est-ce aussi simple que cela, et essayer d'aller plus loin ne ferait qu'ajouter une autre couche d'abstraction pour peu ou pas d'avantages?
la source
eval
... pourrait ne pas fonctionner en Java mais peut-être dans d'autres langages?Réponses:
Bien sûr que non. Même si vous utilisez un conteneur IoC, vous devrez avoir des conditions quelque part, décider de l'implémentation concrète à injecter. Telle est la nature du modèle de stratégie.
Je ne vois pas vraiment pourquoi les gens pensent que c'est un problème. Il y a des déclarations dans certains livres, comme le refactoring de Fowler , que si vous voyez un commutateur / boîtier ou une chaîne de if / elses au milieu d'un autre code, vous devriez considérer cela comme une odeur et chercher à le déplacer vers sa propre méthode. Si le code dans chaque cas est plus d'une ligne, peut-être deux, alors vous devriez envisager de faire de cette méthode une méthode d'usine, renvoyant des stratégies.
Certaines personnes ont pris cela pour signifier qu'un interrupteur / boîtier est mauvais. Ce n'est pas le cas. Mais il devrait résider seul, si possible.
la source
Oui , en utilisant une table de hachage / dictionnaire où s'inscrit chaque implémentation de la stratégie. La méthode d'usine deviendra quelque chose comme
Chaque implémentation de stratégie doit enregistrer une usine avec son orderType et quelques informations sur la création d'une classe.
Vous pouvez utiliser le constructeur statique pour l'enregistrement, si votre langue le prend en charge.
La méthode register ne fait rien de plus que d'ajouter une nouvelle valeur à la table de hachage:
[mise à jour 2012-05-04]
Cette solution est beaucoup plus complexe que la "solution de commutation" d'origine que je préférerais la plupart du temps.
Cependant, dans un environnement où les stratégies changent souvent (c.-à-d. Calcul des prix en fonction du client, du temps, ....), cette solution de hachage associée à un conteneur IoC pourrait être une bonne solution.
la source
factory.register(NEW_ORDER, NewOrder.class);
nettoyant, ou moins une violation de l'OCP, sont-elles exactement que les rangées decase NEW_ORDER: return new NewOrder();
?La «stratégie» consiste à devoir choisir entre des algorithmes alternatifs au moins une fois , pas moins. Quelque part dans votre programme, quelqu'un doit prendre une décision - peut-être l'utilisateur ou votre programme. Si vous utilisez un IoC, une réflexion, un évaluateur de fichier de données ou une construction switch / case pour implémenter cela ne change pas la situation.
la source
Le modèle de stratégie est utilisé lorsque vous spécifiez des comportements possibles et il est préférable de l'utiliser lors de la désignation d'un gestionnaire au démarrage. Spécifier quelle instance de la stratégie à utiliser peut être effectuée via un médiateur, votre conteneur IoC standard, une usine comme vous le décrivez, ou simplement en utilisant la bonne en fonction du contexte (car souvent, la stratégie est fournie dans le cadre d'une utilisation plus large de la classe qui le contient).
Pour ce type de comportement où différentes méthodes doivent être appelées en fonction des données, je suggère d'utiliser les constructions de polymorphisme fournies par le langage en question; pas le modèle de stratégie.
la source
En discutant avec d'autres développeurs où je travaille, j'ai trouvé une autre solution intéressante - spécifique à Java, mais je suis sûr que l'idée fonctionne dans d'autres langages. Construisez une énumération (ou une carte, peu importe laquelle) en utilisant les références de classe et instanciez en utilisant la réflexion.
Cela réduit considérablement le code d'usine:
(Veuillez ignorer la mauvaise gestion des erreurs - exemple de code :))
Ce n'est pas le plus flexible, car une construction est toujours requise ... mais elle réduit le changement de code à une seule ligne. J'aime que les données soient séparées du code d'usine. Vous pouvez même faire des choses comme définir des paramètres en utilisant des classes / interfaces abstraites pour fournir une méthode commune ou en créant des annotations au moment de la compilation pour forcer des signatures de constructeur spécifiques.
la source
L'extensibilité peut être améliorée en faisant en sorte que chaque classe définisse son propre type d'ordre. Ensuite, votre usine sélectionne celle qui correspond.
Par exemple:
la source
Je pense qu'il devrait y avoir une limite au nombre de succursales acceptables.
Par exemple, si j'ai plus de huit cas à l'intérieur de mon instruction switch, je réévalue mon code et cherche ce que je peux re-factoriser. Très souvent, je trouve que certains cas peuvent être regroupés dans une usine distincte. Mon hypothèse ici est qu'il existe une usine qui élabore des stratégies.
Quoi qu'il en soit, vous ne pouvez pas éviter cela et quelque part, vous devrez vérifier l'état ou le type de l'objet avec lequel vous travaillez. Vous allez ensuite construire une stratégie pour cela. Bonne vieille séparation des préoccupations.
la source