Quels sont les moyens d'éliminer l'utilisation du commutateur dans le code?
design-patterns
Mariano
la source
la source
Réponses:
Les instructions Switch ne sont pas un anti-modèle en soi, mais si vous codez en orienté objet, vous devriez vous demander si l'utilisation d'un switch est mieux résolue avec le polymorphisme au lieu d'utiliser une instruction switch.
Avec le polymorphisme, ceci:
devient ceci:
la source
typeof
, et cette réponse ne suggère pas de moyens ou de raisons de contourner les instructions switch dans d'autres situations.Voir l' odeur des déclarations Switch :
Les deux refactorisation et refactoring pour modèles ont des approches pour résoudre ce problème.
Si votre (pseudo) code ressemble à:
Ce code viole le principe ouvert fermé et est fragile à chaque nouveau type de code d'action qui se présente. Pour remédier à cela, vous pouvez introduire un objet 'Command':
Si votre (pseudo) code ressemble à:
Ensuite, vous pouvez introduire un objet «État».
J'espère que cela t'aides.
la source
Map<Integer, Command>
pas besoin d'un commutateur?Un commutateur est un modèle, qu'il soit implémenté avec une instruction switch, une chaîne if else, une table de recherche, un polymorphisme oop, une correspondance de modèle ou autre.
Voulez-vous éliminer l'utilisation de l '" instruction switch " ou du " modèle de commutation "? Le premier ne peut être éliminé, le second, que si un autre modèle / algorithme peut être utilisé, et la plupart du temps ce n'est pas possible ou ce n'est pas une meilleure approche pour le faire.
Si vous souhaitez éliminer l' instruction switch du code, la première question à poser est de savoir où est -il judicieux d'éliminer l'instruction switch et d'utiliser une autre technique. Malheureusement, la réponse à cette question est spécifique au domaine.
Et rappelez-vous que les compilateurs peuvent effectuer diverses optimisations pour changer d'instructions. Ainsi, par exemple, si vous souhaitez traiter efficacement les messages, une instruction switch est à peu près la voie à suivre. Mais d'un autre côté, exécuter des règles métier basées sur une instruction switch n'est probablement pas la meilleure façon de procéder et l'application doit être ré-recherchée.
Voici quelques alternatives à l'instruction switch:
la source
Changer en soi n'est pas si mal, mais si vous avez beaucoup de "switch" ou "if / else" sur les objets dans vos méthodes, cela peut être un signe que votre conception est un peu "procédurale" et que vos objets ne sont que de la valeur seaux. Déplacez la logique vers vos objets, invoquez une méthode sur vos objets et laissez-les décider comment répondre à la place.
la source
Je pense que le meilleur moyen est d'utiliser une bonne carte. En utilisant un dictionnaire, vous pouvez mapper presque n'importe quelle entrée à une autre valeur / objet / fonction.
votre code ressemblerait à quelque chose (psuedo) comme ceci:
la source
Tout le monde aime les
if else
blocs ÉNORMES . Si facile à lire! Je suis cependant curieux de savoir pourquoi vous voudriez supprimer les instructions switch. Si vous avez besoin d'une instruction switch, vous avez probablement besoin d'une instruction switch. Sérieusement, je dirais que cela dépend de ce que fait le code. Si tout le commutateur fait appel à des fonctions (disons), vous pouvez passer des pointeurs de fonction. Que ce soit une meilleure solution est discutable.La langue est un facteur important ici aussi, je pense.
la source
Je pense que ce que vous recherchez, c'est le modèle de stratégie.
Cela peut être implémenté de plusieurs manières, qui ont été mentionnées dans d'autres réponses à cette question, telles que:
la source
switch
Il serait bon de remplacer les instructions si vous vous surprenez à ajouter de nouveaux états ou un nouveau comportement aux instructions:Ajouter un nouveau comportement nécessite de copier le
switch
, et ajouter de nouveaux états signifie en ajouter un autrecase
à chaqueswitch
instruction.En Java, vous ne pouvez changer qu'un nombre très limité de types primitifs dont vous connaissez les valeurs au moment de l'exécution. Cela pose un problème en soi: les états sont représentés sous forme de nombres ou de caractères magiques.
L'appariement de modèles et plusieurs
if - else
blocs peuvent être utilisés, bien qu'ils aient vraiment les mêmes problèmes lors de l'ajout de nouveaux comportements et de nouveaux états.La solution que d'autres ont suggérée comme "polymorphisme" est une instance du modèle d'état :
Remplacez chacun des états par sa propre classe. Chaque comportement a sa propre méthode sur la classe:
Chaque fois que vous ajoutez un nouvel état, vous devez ajouter une nouvelle implémentation de l'
IState
interface. Dans unswitch
monde, vous ajouteriez uncase
à chacunswitch
.Chaque fois que vous ajoutez un nouveau comportement, vous devez ajouter une nouvelle méthode à l'
IState
interface et à chacune des implémentations. C'est le même fardeau qu'avant, bien que maintenant le compilateur vérifie que vous avez des implémentations du nouveau comportement sur chaque état préexistant.D'autres ont déjà dit que cela peut être trop lourd, donc bien sûr, il y a un point où vous atteignez où vous passez de l'un à l'autre. Personnellement, la deuxième fois que j'écris un switch, c'est le moment où je refactore.
la source
sinon
Je réfute la prémisse selon laquelle le changement est intrinsèquement mauvais.
la source
Eh bien, d'une part, je ne savais pas que l'utilisation de switch était un anti-pattern.
Deuxièmement, switch peut toujours être remplacé par des instructions if / else if.
la source
Pourquoi voulez-vous? Entre les mains d'un bon compilateur, une instruction switch peut être beaucoup plus efficace que les blocs if / else (en plus d'être plus facile à lire), et seuls les plus gros commutateurs sont susceptibles d'être accélérés s'ils sont remplacés par une sorte de la structure de données de recherche indirecte.
la source
«switch» est juste une construction de langage et toutes les constructions de langage peuvent être considérées comme des outils pour faire un travail. Comme pour les vrais outils, certains outils sont mieux adaptés à une tâche qu'à une autre (vous n'utiliseriez pas de marteau pour installer un crochet). La partie importante est de savoir comment «faire le travail» est défini. Doit-il être maintenable, doit-il être rapide, doit-il évoluer, doit-il être extensible, etc.
À chaque étape du processus de programmation, il existe généralement une gamme de constructions et de modèles qui peuvent être utilisés: un commutateur, une séquence if-else-if, des fonctions virtuelles, des tables de sauts, des cartes avec des pointeurs de fonction, etc. Avec l'expérience, un programmeur connaîtra instinctivement le bon outil à utiliser pour une situation donnée.
Il faut supposer que quiconque maintient ou révise le code est au moins aussi qualifié que l'auteur original afin que toute construction puisse être utilisée en toute sécurité.
la source
Si le commutateur est là pour faire la distinction entre différents types d'objets, il vous manque probablement des classes pour décrire précisément ces objets, ou des méthodes virtuelles ...
la source
Pour C ++
Si vous faites référence à ie un AbstractFactory, je pense qu'une méthode registerCreatorFunc (..) est généralement meilleure que de demander d'ajouter un cas pour chaque "nouvelle" déclaration qui est nécessaire. Puis laisser toutes les classes créer et enregistrer une creatorFunction (..) qui peut être facilement implémentée avec une macro (si j'ose le mentionner). Je pense que c'est une approche courante que font de nombreux cadres. Je l'ai vu pour la première fois dans ET ++ et je pense que de nombreux frameworks qui nécessitent une macro DECL et IMPL l'utilisent.
la source
Dans un langage procédural, comme C, alors le commutateur sera meilleur que n'importe laquelle des alternatives.
Dans un langage orienté objet, il existe presque toujours d'autres alternatives disponibles qui utilisent mieux la structure de l'objet, en particulier le polymorphisme.
Le problème avec les instructions de commutation se produit lorsque plusieurs blocs de commutation très similaires se produisent à plusieurs endroits dans l'application et que la prise en charge d'une nouvelle valeur doit être ajoutée. Il est assez courant pour un développeur d'oublier d'ajouter la prise en charge de la nouvelle valeur à l'un des blocs de commutateurs dispersés dans l'application.
Avec le polymorphisme, une nouvelle classe remplace la nouvelle valeur et le nouveau comportement est ajouté dans le cadre de l'ajout de la nouvelle classe. Le comportement à ces points de commutation est alors soit hérité de la superclasse, remplacé pour fournir un nouveau comportement, soit implémenté pour éviter une erreur du compilateur lorsque la super méthode est abstraite.
Là où il n'y a pas de polymorphisme évident, cela peut valoir la peine de mettre en œuvre le modèle de stratégie .
Mais si votre alternative est un gros bloc IF ... ALORS ... ELSE, alors oubliez-le.
la source
Utilisez un langage qui n'est pas fourni avec une instruction switch intégrée. Je pense à Perl 5.
Sérieusement, pourquoi voudriez-vous l'éviter? Et si vous avez de bonnes raisons de l'éviter, pourquoi ne pas simplement l'éviter alors?
la source
Les pointeurs de fonction sont un moyen de remplacer une énorme instruction switch, ils sont particulièrement utiles dans les langages où vous pouvez capturer des fonctions par leurs noms et créer des trucs avec elles.
Bien sûr, vous ne devez pas forcer les instructions switch à sortir de votre code, et il y a toujours une chance que vous le fassiez mal, ce qui se traduit par des morceaux de code stupides et redondants. (C'est parfois inévitable, mais un bon langage devrait vous permettre de supprimer la redondance tout en restant propre.)
Voici un excellent exemple de division et de conquête:
Disons que vous avez une sorte d'interprète.
Au lieu de cela, vous pouvez utiliser ceci:
Notez que je ne sais pas comment supprimer la redondance de l'opcode_table en C. Peut-être que je devrais poser une question à ce sujet. :)
la source
La réponse la plus évidente, indépendante de la langue, est d'utiliser une série de «si».
Si le langage que vous utilisez a des pointeurs de fonction (C) ou des fonctions qui sont des valeurs de 1ère classe (Lua), vous pouvez obtenir des résultats similaires à un "commutateur" en utilisant un tableau (ou une liste) de (pointeurs vers) des fonctions.
Vous devriez être plus précis sur la langue si vous voulez de meilleures réponses.
la source
Les instructions Switch peuvent souvent être remplacées par une bonne conception OO.
Par exemple, vous disposez d'une classe Account et utilisez une instruction switch pour effectuer un calcul différent en fonction du type de compte.
Je suggérerais que cela soit remplacé par un certain nombre de classes de compte, représentant les différents types de compte, et implémentant toutes une interface de compte.
Le basculement devient alors inutile, car vous pouvez traiter tous les types de comptes de la même manière et grâce au polymorphisme, le calcul approprié sera exécuté pour le type de compte.
la source
Cela dépend de la raison pour laquelle vous souhaitez le remplacer!
De nombreux interpréteurs utilisent des 'gotos calculés' au lieu d'instructions switch pour l'exécution d'opcode.
Ce qui me manque dans le commutateur C / C ++, c'est le Pascal 'in' et les plages. J'aimerais aussi pouvoir activer les cordes. Mais ceux-ci, bien que simples à manger pour un compilateur, sont un travail difficile lorsqu'ils utilisent des structures, des itérateurs et des choses. Donc, au contraire, il y a plein de choses que j'aimerais pouvoir remplacer par un switch, si seulement le switch C () était plus flexible!
la source
Switch n'est pas une bonne façon de procéder car il brise le principal Open Close. Voilà comment je fais.
Et voici comment l'utiliser (en prenant votre code):
En gros, ce que nous faisons, c'est déléguer la responsabilité à la classe enfant au lieu de laisser le parent décider quoi faire des enfants.
Vous voudrez peut-être également lire le "Principe de substitution de Liskov".
la source
En JavaScript utilisant un tableau associatif:
ceci:
devient ceci:
Courtoisie
la source
Un autre vote pour if / else. Je ne suis pas un grand fan des déclarations de cas ou d'interrupteurs, car certaines personnes ne les utilisent pas. Le code est moins lisible si vous utilisez le cas ou le commutateur. Peut-être pas moins lisible pour vous, mais pour ceux qui n'ont jamais eu besoin d'utiliser la commande.
Il en va de même pour les usines d'objets.
Les blocs if / else sont une construction simple que tout le monde obtient. Il y a quelques choses que vous pouvez faire pour vous assurer de ne pas causer de problèmes.
Premièrement, n'essayez pas d'indenter les déclarations if plus d'une ou deux fois. Si vous vous trouvez en retrait, c'est que vous le faites mal.
Est vraiment mauvais - faites-le à la place.
L'optimisation soit damnée. Cela ne fait pas beaucoup de différence pour la vitesse de votre code.
Deuxièmement, je ne suis pas opposé à la sortie d'un bloc If tant qu'il y a suffisamment d'instructions break dispersées dans le bloc de code particulier pour le rendre évident
EDIT : Sur Switch et pourquoi je pense qu'il est difficile de grok:
Voici un exemple d'instruction switch ...
Pour moi, le problème ici est que les structures de contrôle normales qui s'appliquent dans les langages de type C ont été complètement brisées. Il existe une règle générale selon laquelle si vous souhaitez placer plus d'une ligne de code dans une structure de contrôle, vous utilisez des accolades ou une instruction de début / fin.
par exemple
Pour moi (et vous pouvez me corriger si je me trompe), l'instruction Case jette cette règle hors de la fenêtre. Un bloc de code exécuté conditionnellement n'est pas placé dans une structure de début / fin. Pour cette raison, je pense que Case est conceptuellement suffisamment différent pour ne pas être utilisé.
Espérons que cela répond à vos questions.
la source