Je lisais ceci article et je me demandais, nous débarrassons-nous de toutes les instructions switch en les remplaçant par un dictionnaire ou une fabrique afin qu'il n'y ait aucune instruction switch dans mes projets.
Quelque chose ne s'additionnait pas vraiment.
La question est de savoir si les instructions switch ont une utilité réelle ou allons-nous les remplacer par un dictionnaire ou une méthode d'usine (en utilisant une méthode d'usine, bien sûr, il y aura une utilisation minimale des instructions switch pour créer les objets en utilisant l'usine ... mais c'est à peu près tout).
Les instructions switch
et le polymorphisme ont tous deux leur utilité. Notez cependant qu'une troisième option existe également (dans les langages qui prennent en charge les pointeurs de fonction/lambdas et les fonctions d'ordre supérieur): mapper les identificateurs en question aux fonctions de gestionnaire. Ceci est disponible par ex. C qui n'est pas un langage OO, et C # qui est *, mais pas (encore) en Java qui est OO = trop *.
Dans certains langages procéduraux (n'ayant ni polymorphisme ni fonctions d'ordre supérieur) switch
/if-else
les instructions étaient le seul moyen de résoudre une classe de problèmes. Tant de développeurs, habitués à cette façon de penser, ont continué à utiliser switch
même dans les langues OO, où le polymorphisme est souvent une meilleure solution. C'est pourquoi il est souvent recommandé pour éviter/refactoriser les instructions switch
en faveur du polymorphisme.
Quoi qu'il en soit, la meilleure solution dépend toujours de la casse. La question est: quelle option vous donne un code plus propre, plus concis et plus maintenable à long terme?
Les instructions de commutation peuvent souvent devenir lourdes, avec des dizaines de cas, ce qui rend leur maintenance difficile. Comme vous devez les conserver dans une seule fonction, cette fonction peut devenir énorme. Si tel est le cas, vous devriez envisager une refactorisation vers une solution basée sur une carte et/ou polymorphe.
Si le même switch
commence à apparaître à plusieurs endroits, le polymorphisme est probablement la meilleure option pour unifier tous ces cas et simplifier le code. Surtout si d'autres cas devraient être ajoutés à l'avenir; plus vous avez besoin de mises à jour à chaque fois, plus il y a de possibilités d'erreurs. Cependant, souvent les gestionnaires de cas individuels sont si simples, ou il y en a tellement, ou ils sont tellement interdépendants, que leur refactorisation dans une hiérarchie de classes polymorphes complète est exagéré, ou entraîne beaucoup de code dupliqué et/ou emmêlé, difficile de maintenir la hiérarchie des classes. Dans ce cas, il peut être plus simple d'utiliser à la place des fonctions/lambdas (si votre langue le permet).
Cependant, si vous avez un switch
en un seul endroit, avec seulement quelques cas faisant quelque chose de simple, ce pourrait être la meilleure solution pour le laisser tel quel.
*J'utilise le terme "OO" de manière lâche ici; Je ne suis pas intéressé par les débats conceptuels sur ce qui est "réel" ou "pur" OO.
C'est là que je redeviens dinosaure ...
Les instructions Switch ne sont pas mauvaises en soi, c'est l'utilisation qui en est faite qui est en cause.
La plus évidente est la "même" instruction switch répétée maintes et maintes fois dans votre code qui est mauvaise (là-bas, fait cela, ferait tout son possible pour ne pas recommencer) - et c'est ce dernier cas que vous pourrez peut-être traiter avec l'utilisation du polymorphisme. Il y a généralement quelque chose d'assez horrible dans les cas imbriqués aussi (j'avais l'habitude d'avoir un monstre absolu - je ne sais pas trop comment je le gère maintenant autre que "mieux").
Dictionnaire comme commutateur Je trouve plus difficile - fondamentalement oui si votre commutateur couvre 100% des cas, mais là où vous voulez avoir des cas par défaut ou sans action, cela commence à devenir un peu plus intéressant.
Je pense que c'est une question d'éviter la répétition et de nous assurer que nous composons nos graphiques d'objets aux bons endroits.
Mais il y a aussi l'argument de compréhension (maintenabilité) et cela coupe dans les deux sens - une fois que vous comprenez comment tout cela fonctionne (le modèle et l'application dans laquelle il est implémenté), c'est facile ... mais si vous arrivez à une seule ligne de code où vous devez ajouter quelque chose de nouveau, vous devez ensuite sauter partout pour déterminer ce que vous devez ajouter/modifier.
Pour autant que nos environnements de développement soient massivement capables, je pense toujours qu'il est souhaitable de pouvoir comprendre le code (comme s'il était) imprimé sur du papier - pouvez-vous suivre le code avec votre doigt? J'accepte qu'en fait non, avec beaucoup de bonnes pratiques aujourd'hui, vous ne pouvez pas et pour de bonnes raisons, mais cela signifie qu'il est plus difficile de se familiariser avec le code (ou peut-être que je suis juste vieux ...)
Switch-statement vs subtype-polymorphism est un vieux problème et est souvent mentionné dans la communauté FP dans les discussions sur le Problème d'expression .
Fondamentalement, nous avons des types (classes) et des fonctions (méthodes). Comment codons-nous des choses pour qu'il soit facile d'ajouter de nouveaux types ou de nouvelles méthodes?
Si vous programmez dans le style OO, il est difficile d'ajouter une nouvelle méthode (car cela signifierait une refactorisation de toutes les classes existantes), mais il est très facile d'ajouter de nouvelles classes qui utilisent les mêmes méthodes qu'auparavant .
D'un autre côté, si vous utilisez une instruction switch (ou son équivalent OO équivalent, le modèle d'observateur), il est très facile d'ajouter de nouvelles fonctions mais il est difficile d'ajouter de nouveaux cas/classes.
Il n'est pas facile d'avoir une bonne extensibilité dans les deux sens, donc lors de l'écriture de votre code, déterminez s'il faut utiliser le polymorphisme ou basculer les instructions en fonction de la direction dans laquelle vous êtes le plus susceptible d'étendre ce dernier.
nous débarrassons-nous de toutes les instructions switch en les remplaçant par un dictionnaire ou une fabrique afin qu'il n'y ait aucune instruction switch dans mes projets.
Non. De tels absolus sont rarement une bonne idée.
À de nombreux endroits, une répartition dictionnaire/recherche/usine/polymorphe fournira une meilleure conception qu'une instruction switch mais vous devez toujours remplir le dictionnaire. Dans certains cas, cela obscurcira ce qui se passe réellement et le simple fait d'avoir l'instruction switch en ligne est plus lisible et maintenable.
Étant donné que cela est indépendant du langage, le code de transition fonctionne mieux avec les instructions switch
:
switch(something) {
case 1:
foo();
case 2:
bar();
baz();
break;
case 3:
bang();
default:
bizzap();
break;
}
Équivalent à if
, avec un cas par défaut très maladroit. Et gardez à l'esprit que plus il y a de retombées, plus la liste de conditions sera longue:
if (1 == something) {
foo();
}
if (1 == something || 2 == something) {
bar();
baz();
}
if (3 == something) {
bang();
}
if (1 != something && 2 != something) {
bizzap();
}
(Cependant, compte tenu de ce que disent certaines des autres réponses, j'ai l'impression d'avoir raté le point de la question ...)
Les instructions Switch car la différenciation des cas ont une réelle utilité. Les langages de programmation fonctionnels utilisent quelque chose appelé correspondance de modèle qui vous permet de définir des fonctions différemment selon l'entrée. Cependant, dans les langages orientés objet, le même objectif peut être atteint plus élégamment en utilisant le polymorphisme. Vous appelez simplement la méthode et selon le type réel de l'objet, l'implémentation correspondante de la méthode sera exécutée. C'est la raison derrière l'idée, que les instructions switch sont une odeur de code. Cependant, même dans les langues OO, vous pouvez les trouver utiles pour implémenter le modèle d'usine abstrait.
tl; dr - ils sont utiles, mais vous pouvez souvent faire mieux.