web-dev-qa-db-fra.com

Échec vs relance en Ruby: Faut-il vraiment croire le guide de style?

Ruby offre deux possibilités pour provoquer une exception par programme: raise et fail, les deux étant des méthodes Kernel. Selon les documents, ils sont absolument équivalents.

Par habitude, je n'ai utilisé que raise jusqu'à présent. Maintenant, j'ai trouvé plusieurs recommandations (par exemple ici ), pour utiliser raise pour les exceptions à détecter, et fail pour les erreurs graves qui ne sont pas destinées à être gérées.

Mais est-ce vraiment logique? Lorsque vous écrivez une classe ou un module et que vous causez un problème au plus profond de vous, que vous signalez par fail, vos collègues de programmation qui examinent le code, peuvent heureusement comprendre vos intentions, mais la personne qui est en utilisant mon code ne verra probablement pas mon code et n'a aucun moyen de savoir si l'exception a été provoquée par un raise ou par fail. Par conséquent, mon utilisation prudente de raise ou fail ne peut avoir aucune influence sur sa décision, qu'elle le fasse ou non.

Quelqu'un pourrait-il voir des défauts dans mes arguments? Ou existe-t-il d'autres critères qui pourraient me permettre d'utiliser fail au lieu de raise?

34
user1934428

utilisez "augmenter" pour que les exceptions soient détectées et "échouer" pour les erreurs graves qui ne sont pas censées être traitées

Ce n'est pas ce que le guide de style officiel ou le lien que vous avez fourni disent à ce sujet.

Ce que l'on veut dire ici, c'est utiliser raise uniquement dans les blocs rescue. Aka utilisez fail lorsque vous voulez dire que quelque chose est échec et utilisez raise lorsque rethrowing une exception.

Quant à la partie "est-ce important" - ce n'est pas l'une des règles les plus strictes strictement suivies, mais vous pouvez faire le même argument pour n'importe quelle convention. Vous devez suivre dans cet ordre:

  1. Votre guide de style de projet
  2. Votre guide de style d'entreprise
  3. Le guide du style communautaire

Idéalement, les trois devraient être les mêmes.


Mise à jour : Depuis ce PR (décembre 2015), la convention est de toujours utiliser raise.

44
ndnenkov

J'ai eu une fois une conversation avec Jim Weirich à propos de cette chose, j'ai toujours utilisé fail lorsque ma méthode échoue explicitement pour une raison quelconque et raise pour exceptions levées.

Voici un article avec un message de Jim (presque mot pour mot ce qu'il m'a dit en personne): http://www.virtuouscode.com/2014/05/21/jim-weirich-on-exceptions/

Voici le texte pertinent de l'article, une citation attribuée à Jim:

Voici ma philosophie de base (et d'autres pensées aléatoires) sur les exceptions.

Lorsque vous appelez une méthode, vous avez certaines attentes quant à ce que la méthode accomplira. Officiellement, ces attentes sont appelées post-conditions. Une méthode doit lever une exception chaque fois qu'elle ne remplit pas ses conditions postérieures.

Pour utiliser efficacement cette stratégie, cela implique que vous devez avoir une petite compréhension de la conception par contrat et de la signification des conditions préalables et postérieures. Je pense que c'est une bonne chose à savoir de toute façon.

Voici quelques exemples concrets. La méthode Rails model save:

model.save!
-- post-condition: The model object is saved.

Si le modèle n'est pas enregistré pour une raison quelconque, une exception doit être déclenchée car la post-condition n'est pas remplie.

model.save
-- post-condition: (the model is saved && result == true) ||
                   (the model is not saved && result == false)

Si save n'enregistre pas réellement, le résultat renvoyé sera false, mais la post-condition est toujours remplie, donc aucune exception.

Je trouve intéressant que le save! La méthode a une post-condition beaucoup plus simple.

Au sujet du sauvetage des exceptions, je pense qu'une application devrait avoir des points stratégiques où les exceptions sont sauvées. La plupart du temps, il n'y a guère besoin de sauvetage/retour. La seule fois où vous voudriez sauver et retourner, c'est lorsque vous avez terminé un travail à mi-chemin et que vous voulez annuler quelque chose afin d'éviter un état partiellement terminé. Vos points de sauvetage stratégiques doivent être choisis avec soin afin que le programme puisse continuer avec d'autres travaux même si l'opération en cours a échoué. Les programmes de traitement des transactions devraient simplement passer à la prochaine transaction. Une application Rails devrait récupérer et être prête à gérer la prochaine requête http.

La plupart des gestionnaires d'exceptions doivent être génériques. Étant donné que les exceptions indiquent une défaillance d'un certain type, le gestionnaire n'a qu'à prendre une décision sur ce qu'il faut faire en cas d'échec. Les opérations de récupération détaillées pour des exceptions très spécifiques sont généralement déconseillées, sauf si le gestionnaire est très proche (au niveau du graphique d'appel) du point de l'exception.

Les exceptions ne doivent pas être utilisées pour le contrôle de flux, utilisez throw/catch pour ça. Cela réserve des exceptions pour de véritables conditions de défaillance.

(Un côté, parce que j'utilise des exceptions pour indiquer des échecs, j'utilise presque toujours le mot clé fail plutôt que le mot clé raise dans Ruby. Fail et raise sont des synonymes, donc il n'y a pas de différence, sauf que fail communique plus clairement que la méthode a échoué. La seule fois où j'utilise raise, c'est quand j'attrape une exception et la relance, car ici Je n'échoue pas , mais je soulève explicitement et délibérément une exception. C'est un problème de style que je suis, mais je doute que beaucoup d'autres personnes le fassent).

Voilà, un souvenir plutôt décousu de mes réflexions sur les exceptions.

Je sais qu'il existe de nombreux guides de style qui ne sont pas d'accord (le guide de style utilisé par RoboCop , par exemple). Je m'en fiche. Jim m'a convaincu.

4
Karl Wilbur