Est-il nécessaire d'ajouter le cas par défaut lors de l'utilisation de cas de commutateur?

41

Lors d'une récente révision du code, on m'a demandé de placer des defaultcas dans tous les fichiers, quel que soit le switchbloc utilisé, même s'il n'y a rien à faire default. Cela signifie que je dois mettre le defaultcas et ne rien écrire dedans.

Est-ce la bonne chose à faire? A quoi cela servirait-il?

Ankit
la source
9
C'est à vous ... Je veux dire le style de code du superviseur.
SD le
1
Je pense qu'il y a même des cas où vous ne voulez pas d'un cas par défaut. Pensez à basculer une enum. Votre commutateur définit un cas pour toutes les valeurs possibles. Maintenant, ajouter une valeur à une énumération devrait entraîner l’ajout d’une observation dans ce commutateur. Si vous ne le faites pas, cela pourrait être une erreur, car vous voulez faire quelque chose pour cette nouvelle valeur enum, mais votre code ne le fait pas. Votre compilateur peut vous avertir (je pense; je ne suis pas sûr de Java) à ce sujet, mais uniquement si vous n'avez pas mis de cas par défaut. Vous pouvez également imprimer un avertissement dans le cas par défaut lorsque vous êtes certain que le code ne l’atteint jamais.
leemes
6
Mes amis et moi appelons cela le cas "exception de développeur" - lorsque quelque chose dans le code survient qui ne devrait jamais se produire (pour des raisons logiques / de code), nous ajoutons alors une "exception de développeur". De cette façon, si cela se produit, une exception de développeur est générée et au moins nous savons où les choses se sont mal passées.
Marco

Réponses:

31

Il semble qu'il y ait trois cas où une defaultdéclaration n'est pas nécessaire:

  1. aucun autre cas n'est laissé, car il y a un ensemble limité de valeurs qui entrent dans le fichier switch case. Mais cela pourrait changer avec le temps (intentionnellement ou accidentellement), et il serait bon d’avoir un default caseéventuel changement, vous pouvez enregistrer ou avertir l’utilisateur d’une valeur erronée.

  2. vous savez comment et où le switch casesera utilisé et quelles valeurs le saisiront. Là encore, cela pourrait changer et un traitement supplémentaire pourrait être nécessaire.

  3. les autres cas ne nécessitent aucun traitement spécial. Si tel est le cas, je pense que vous êtes invité à ajouter un default case, car il s'agit d'un style de codage accepté, ce qui rend votre code plus lisible.

Les deux premiers cas sont basés sur des hypothèses. Donc (en supposant que vous travailliez dans une équipe pas si petite puisque vous avez des revues de code régulières), vous ne pouvez pas vous permettre de faire ces hypothèses. Vous ne savez pas qui travaillera avec votre code ou effectuera des appels à des fonctions / invoquant des méthodes dans votre code. De même, vous devrez peut-être utiliser le code de quelqu'un d'autre. Le même style de codage facilitera la gestion du code de quelqu'un (y compris votre code).

SuperM
la source
14
Dans les cas 1 et 2, un cas par défaut devrait être une erreur. Si votre style de codage utilise toujours la valeur par défaut, faites-le en émettant une erreur entre 1 et 2. Il est possible de le faire au moment de la compilation, mais cela pourrait ne pas toujours être possible, même en java. Je pense au moins qu'il est important que l'utilisateur reçoive le message suivant: "Vous vous tirez une balle dans le pied en transmettant cette valeur"
mardi
11
Un cas par défaut est toujours une bonne idée, car, par exemple, dans le cas d'un enum, si quelqu'un ajoute une nouvelle entrée à l'énum, ​​votre cas par défaut doit faire quelque chose de similaire à throw new IllegalStateException("Unrecognised case "+myEnum)ce qui vous indique où et quelle est l'erreur.
Rich
Cela signifie-t-il que vous devriez toujours avoir une elseclause après chaque, if/else ifmême si vous savez que cela ne devrait pas arriver? Cela ne semble pas être une bonne idée pour moi ...
jadkik94
2
Si je peux ajouter une anecdote tirée de mon travail: j'avais une méthode factory qui utilisait une énumération pour instancier certaines classes. J'avais laissé des instructions pour que chaque fois que vous ajoutiez une nouvelle classe à ce projet, vous deviez ajouter une valeur à l'énumération et à un cas du commutateur. Bien sûr, une semaine après la fermeture du code, celui-ci cesse de fonctionner, à une exception près qui ne doit jamais être soulevée. Je vais chez le programmeur et lui demande: "as-tu ajouté un cas à l'interrupteur?" et sûrement, il ne l'a pas fait. Ce jour-là, j'ai changé l'exception en disant "si vous recevez ce message, blâmez le dernier développeur qui a touché le code".
Ziv
28

Est-ce le bon thiong à faire? A quoi cela servirait-il?

Il n'est pas rare que les normes de codage des entreprises exigent un cas par défaut pour toutes les switchinstructions. L’une des raisons est qu’il est facile pour les lecteurs de trouver la fin du texte switch. Une autre raison, probablement meilleure, est que cela vous fait réfléchir une seconde sur ce que votre code devrait faire lorsque la condition ne correspond pas à vos attentes. Quelle que soit la raison de l'exigence, s'il s'agit d'une norme de l'entreprise, vous devez la respecter sauf s'il existe une raison étanche à l'air.

Si vous pensez que votre switchcas inclut toutes les conditions possibles, alors une bonne chose à faire est de placer une assertdéclaration dans le cas par défaut. Ainsi, lorsque quelqu'un modifie le code et ajoute par inadvertance une condition que vous switchne couvrez pas, il remplit la assertfenêtre et réalise qu'il doit traiter cette partie du code.

Si vous switchne couvrez que quelques-unes des conditions possibles, mais que rien n'est spécial, vous pouvez laisser le cas par défaut vide. C'est une bonne idée d'ajouter un commentaire dans ce cas pour indiquer que le cas par défaut est intentionnellement vide, car les conditions qui le remplissent ne nécessitent aucun travail.

Caleb
la source
1
Qu'est - ce qu'un assert?
FLY
1
@FLY C'est un mot-clé en Java et généralement une macro en C, C ++, etc. Dans tous les cas, une assertion est utilisée pour tester le fait qu'une hypothèse reste vraie et renvoie une exception ou provoque sinon une erreur, si ce n'est pas le cas.
Caleb
J'ai édité mon commentaire précédent avec un lien vers la wikipedia
FLY
Vous ne devriez pas laisser une caisse vide. Si votre commutateur ne couvre pas toutes les conditions possibles, vous devez affirmer les autres conditions dans votre cas par défaut pour la même raison que vous avez suggéré d'ajouter une assertion dans le cas par défaut dans votre deuxième paragraphe.
Rold2007
12

Si vous "basculez" sur un type d'énumération pur, il est dangereux d'avoir un repli par défaut. Lorsque vous ajouterez ultérieurement des valeurs au type énumération, le compilateur mettra en surbrillance les commutateurs avec les nouvelles valeurs non couvertes. Si vous avez la clause default ici, le compilateur restera silencieux et vous risquez de le manquer.

Artur
la source
1
+1 pour résoudre le problème au moment de la compilation et non au moment de l'exécution.
SylvainD
Je me demande comment une réponse totalement trompeuse est votée et acceptée. La recherche rapide montre que le compilateur java a ce genre d'avertissement. Pourquoi diable allez-vous attendre l'erreur au moment de l'exécution, les gens?
Artur
Je ne connaissais pas ce comportement. Mon exemple de code ideone ideone.com/WvytWq suggère le contraire. En supposant que vous disiez vrai, que se passera-t-il si quelqu'un ajoute des valeurs à un type d'énumération, mais vous ne recompilez pas votre code?
emory
1
De toute évidence, les nouvelles valeurs ne sont tout simplement pas gérées par une instruction switch sans casse par défaut. Mais si vous ne recompilez pas vos implémentations lorsque les interfaces changent, votre système de construction est vraiment en panne.
Artur
9

À bien des égards, cette question est la même que celle souvent posée Ai-je besoin d'une elseclause à la fin d'un if/ else ifladder couvrant toutes les options ?

La réponse est, syntaxiquement, non. Mais il y a cependant ...

Une defaultclause peut être là pour (au moins) deux raisons:

  1. En tant que gestionnaire d'erreur - bien sûr, ne devrait jamais arriver ici! est une revendication qui peut être faite, mais que faire si elle fait? La corruption des données, ou pire encore, l'absence de validation des données sont des voies d'échec du programme si elles ne sont pas piégées correctement Dans ce cas, ce ne devrait pas être une clause vide!
  2. Couverture de conception / code - même sous la forme la plus simple d'un organigramme, il existe deux routes à partir d'une instruction if et toujours otherwised'une casse. Il n'y a aucune raison de ne pas les inclure dans le code source.

Ma philosophie est toujours très simple: évaluez le pire scénario des deux options et optez pour le plus sûr. Dans le cas d'un vide elseou d'une defaultclause, les options les plus défavorables sont:

  • Incluez-les: Trois ou quatre lignes supplémentaires de code "redondant".
  • Ne les incluez pas: des données non fiables ou une condition inattendue ne sont pas interceptées, ce qui peut entraîner une défaillance du programme.

Overdramatic? Peut-être… mais mon logiciel risque alors de tuer des gens s'il échoue. Je préfère ne pas prendre ce risque.


De plus, les directives de la MISRA-C {voir le profil d’affiliation} recommandent une defaultclause pour chaqueswitch

Andrew
la source
Pas seulement au bout d'une échelle. La question est: ai-je besoin d'un elseaprès un if? Mais comme tu l'as dit, non, ça ne l'est pas.
ott--
En tant que gestionnaire d’erreur, je suggère un formulaire légèrement modifié qui évite la valeur par défaut vide. En ce qui concerne le meurtre de personnes, je ne pense pas que ce soit vraiment important - les gens seront tout aussi morts, que vous ayez ou non la clause par défaut, mais il sera plus facile de comprendre pourquoi ils sont morts.
Emory
Bon point, @emory - ce n'était pas très clair, était-ce ... édité :)
Andrew
6

Java ne vous oblige pas à avoir une instruction 'default', mais c'est une bonne pratique d'en avoir une tout le temps, même si le code ne peut jamais être atteint (maintenant). Voici quelques raisons:

En ayant une clause default inaccessible, vous montrez au lecteur de votre code que vous avez considéré les valeurs et que vous savez ce que vous faites. Vous autorisez également les modifications futures, par exemple, par exemple: une nouvelle valeur enum est ajoutée, le commutateur ne doit pas ignorer la nouvelle valeur en mode silencieux; vous pouvez lancer une exception à la place ou faire autre chose.

Pour capturer une valeur inattendue (au cas où vous n'activez pas une énumération) qui est transmise - elle peut être supérieure ou inférieure à celle attendue, par exemple.

Pour gérer les actions "par défaut" - où les commutateurs ont un comportement spécial. Par exemple, une variable peut être déclarée en dehors du commutateur mais pas initialisée et chaque cas l'initialise à quelque chose de différent. Default dans ce cas pourrait l'initialiser à une valeur par défaut afin que le code immédiatement après le commutateur ne commette pas d'erreur / génère une exception.

Même si vous décidez de ne rien mettre dans la valeur par défaut (pas d'exceptions, de journalisation, etc.), même un commentaire indiquant que vous avez pris en compte le fait que la défaillance ne se produira jamais peut améliorer la lisibilité de votre code; mais cela dépend de la préférence personnelle.

Déco
la source
2

J'ajouterais également que cela dépend de la philosophie de la langue que vous utilisez. Dans Erlang, par exemple, où il est courant de «laisser tomber», vous ne définissez pas de cas par défaut, mais laissez votre casedéclaration en erreur si une situation non résolue se produit.

Rodrigue
la source
0

Vous devriez toujours avoir un défaut sauf si vous avez couvert de manière prouvable et permanente tous les cas. Cela ne coûte rien, et c'est une assurance contre le non-maintien de votre relevé de commutateur dans un programme en évolution.

Si vous êtes vraiment sûr que vous ne vous souciez pas d'autres cas, la valeur par défaut: break; // ne vous souciez pas, mais la valeur par défaut devrait ressembler à celle par défaut: throw new Error ("valeur inattendue");

De même, vous ne devriez jamais "passer à travers" une construction de cas non triviale sans ajouter de commentaire à l'effet que vous avez l'intention de faire passer. Java s'est trompé lors de la conception.

Ddyer
la source
-2

Considérer

enum Gender
{
       MALE ,
       FEMALE ;
}

et

void run ( Gender g )
{
      switch ( g )
      {
           case MALE :
                 // code related to males
                 break ;
           default :
                 assert Gender . FEMALE . equals ( g ) ;
                 // code related to females
                 break ;
      }
}

Je dirais que c'est mieux que d'avoir un cas MALE, un cas FEMALE et un cas par défaut qui lève une exception.

Pendant votre phase de test, l’ordinateur vérifiera si g est FEMALE. Pendant la production, le contrôle sera omis. (Ouais une microoptimisation!)

Plus important encore, vous maintiendrez votre objectif de couverture de code à 100%. Si vous écriviez un cas par défaut qui lève une exception, comment le testeriez-vous?

Emory
la source
1
1. Pas besoin de micro-optimisation - l'élimination des codes morts est utilisée dans les compilateurs depuis une trentaine d'années et sera éliminée par l'EJI. 2. Une couverture de code à 100% n'est généralement pas considérée comme importante (d'une part, une couverture à 100% risque de ne pas prendre en compte tous les cas extrêmes; d'autre part, aucun test ne détectera les lignes assert_not_reached du programme correct). Dans ce cas, ne laisser aucun cas par défaut entraînerait une erreur du compilateur. D'autre part, comment vérifier si l'assertion fonctionne (vous déplacez le problème et le cachez avec une couverture de 100% au lieu d'avoir la même ligne "cela ne se produira pas, je vous le promets").
Maciej Piechotka
1
3. Quand Gender . FEMALE . equals ( FEMALE ) ;évalue- falset-on? Vous Gender.FEMALE.equals (g)vouliez dire, mais puisque vous pouvez compter sur des références à la stabilité des enums, vous pouvez simplement écrire g == Gender.FEMALE. 4. (Opinion personnelle), un tel code est beaucoup plus difficile à lire et produira des erreurs légèrement plus source de confusion.
Commentaires
@MaciejPiechotka bonne prise à propos de g. Oui, la microoptimisation est un avantage, mais ce n’est pas non plus un inconvénient. Une couverture de code à 100% est un avantage ssf qui est l'un de vos objectifs et votre outil de couverture de code ne vérifie pas les affirmations (ce qui ne devrait pas être le cas).
Emory
Alors pourquoi serait-ce l’un de mes objectifs? La partie "intéressante" doit être testée pour tous les cas critiques (indépendamment du fait qu'elles correspondent à des lignes de code) et les parties "ennuyeuses" peuvent être simplement ignorées pour gagner du temps sur les tests. La couverture de code IMHO est une mauvaise mesure des tests.
Maciej Piechotka