Comment nommer une méthode qui effectue une tâche et renvoie un booléen sous forme d'état?

33

S'il y a une méthode

bool DoStuff() {
    try {
        // doing stuff...
        return true;
    }
    catch (SomeSpecificException ex) {
        return false;
    }
}

devrait-il plutôt s'appeler IsStuffDone()?

Les deux noms pourraient être mal interprétés par l'utilisateur: Si le nom est: DoStuff()pourquoi renvoie-t-il un booléen? Si le nom est, IsStuffDone()il n'est pas clair si la méthode exécute une tâche ou ne vérifie que son résultat.

Existe-t-il une convention pour ce cas? Ou une approche alternative, comme celle-ci est considérée comme imparfaite? Par exemple, dans les langages ayant des paramètres de sortie, tels que C #, une variable d'état booléen peut être transmise à la méthode sous la forme d'une variable et le type de résultat de la méthode doit être void.

EDIT: Dans mon problème particulier, la gestion des exceptions ne peut pas être directement déléguée à l'appelant, car la méthode fait partie d'une implémentation d'interface. Par conséquent, l'appelant ne peut pas être chargé de traiter toutes les exceptions d'implémentations différentes. Il ne connaît pas ces exceptions. Cependant, l'appelant peut gérer une exception personnalisée, comme StuffHasNotBeenDoneForSomeReasonExceptionsuggéré dans la réponse et le commentaire de npinti .

Exil Limbo
la source
2
Cela dépend de l'utilisation de la fonction et des tâches effectuées. Si cela est nécessaire, les éléments doivent être exécutés correctement, alors je considérerais cette approche comme défectueuse, car l'utilisateur de la fonction risque de manquer le drapeau booléen et manque également des informations fournies par l'exception.
Benni
7
La plupart du temps, j'appellerais cela "cassé", car renvoyer un message booleanau lieu d'envelopper ou de passer l'exception est presque toujours faux.
Maaartinus
2
Ce type de gestionnaire d'exception est rarement une bonne idée. Attrapez uniquement les exceptions spécifiques que vous attendez, pas toutes. Et si possible, évitez de le jeter en premier lieu, en éliminant la nécessité de l'attraper.
CodesInChaos
2
Umm ... BadlyDesignedMethodInSeriousNeedOfRefactoring? Et pour répondre à votre question sur les exceptions - je pouvais laisser l'appelant les gérer ou les intercepter puis émettre une exception personnalisée qui signifie "cette méthode ne fait pas son travail". Partager et profiter.
Bob Jarvis - Réintégrer Monica
5
A tous ceux qui disent: lancez (ou laissez passer) une exception, vous faites des suppositions sans fondement sur la façon dont ce code est utilisé. Un scénario possible est qu’il existe un problème à résoudre, avec diverses méthodes de résolution heuristiques qui résolvent des sous-classes toujours plus grandes du problème à un coût toujours croissant; il serait logique d'écrire quelque chose comme if (FirstMethodSucceeds(problem) or SecondMethodSucceeds(problem) or ...) Hurray(); else UniversalSolve(problem);. Faire la même chose avec des exceptions (personnalisées?) Serait inutilement plus compliqué.
Marc van Leeuwen

Réponses:

68

Dans .NET, vous avez souvent des paires de méthodes dans lesquelles l'une d'elles peut générer une exception ( DoStuff), l'autre renvoie un statut booléen et, en cas d'exécution réussie, le résultat réel via un paramètre out ( TryDoStuff).

(Microsoft appelle cela le "modèle d'analyse par essai" , car l'exemple le plus frappant est celui des TryParseméthodes de divers types primitifs.)

Si le Trypréfixe n'est pas courant dans votre langue, vous ne devriez probablement pas l'utiliser.

Ne2dmar
la source
3
+1 C'est comme ça que j'ai vu ce genre de chose se faire ailleurs. if (TryDoStuff()) print("Did stuff"); else print("Could not do stuff");est un langage assez standard et intuitif à mon avis.
Karl Nicoll
12
Il est à noter que les TryDoStuffméthodes sont supposées échouer proprement et ne produire aucun effet secondaire lorsqu'elles renvoient false.
Trillian
Pour votre information, il existe également des Removeméthodes, par exemple dans le framework .NET, qui suppriment un élément d'une structure de données (par exemple Dictionary) et renvoient un bool. Le consommateur de l'API peut décider de le consommer ou de l'ignorer. Cependant, les erreurs sont signalées comme des exceptions.
Omer Iqbal
18

Et si vous jetiez simplement l'exception au code d'appel?

De cette façon, vous déléguez la gestion des exceptions à quiconque utilise votre code. Et si, dans le futur, vous voudriez faire ce qui suit:

  • Si aucune exception n’est levée, passez à l’action A
  • Si (par exemple) un FileNotFoundExceptionest lancé, passez à l'action B
  • Si une autre exception est générée, passez à l'action C

Si vous rejetez votre exception, la modification ci-dessus impliquerait simplement l'ajout d'un catchbloc supplémentaire . Si vous le laissez tel quel, vous devrez modifier la méthode et l'emplacement à partir duquel la méthode est appelée, qui, selon la complexité du projet, peut se trouver à plusieurs emplacements.

Npinti
la source
1
Que se passe-t-il si l'exception est de type qui ne peut pas être délégué et devrait être traitée en interne?
Limbo Exile
2
@LimboExile: Je pense que vous pourriez toujours en traiter en interne et ensuite, peut-être lancer une autre exception, peut-être la vôtre. Cela illustrerait le fait que quelque chose qui n'aurait pas dû se produire aurait eu lieu, mais en même temps, vous n'avez pas exposé ce qui se passe réellement sous le capot, ce qui, je suppose, est la raison pour laquelle vous souhaitez traiter l'exception à l'intérieur.
npinti
6
Il convient peut-être de garder à l'esprit que le fait de lancer une exception devrait être réservé à un cas exceptionnel . S'il est courant ou normal DoStuffd'échouer, le lancement d'une exception pour le cas d'échec équivaudrait à contrôler le déroulement du programme avec des exceptions qui sont mauvaises. Les exceptions ont également un coût de performance inhérent. Si DoStufféchouer est un cas rare dû à une erreur, alors les exceptions sont définitivement la voie à suivre, comme le suggère @npinti.
Karl Nicoll
1
@KarlNicoll Que quelque chose soit "exceptionnel" est totalement subjectif. Le critère principal doit être de savoir si vous voulez que le programme se bloque si personne ne fait quelque chose à propos de l'erreur et si vous voulez que la possibilité d'erreur soit présente dans le type de la fonction. De plus, ce n’est pas un fait que les exceptions coûtent cher; cela dépend de la langue et de la façon dont vous les lancez. Les exceptions ne coûtent pas cher en Python, et si vous supprimez les informations de trace de la pile, elles peuvent l'être également en Java. Même s'il y avait un coût de performance, il est prématuré de s'en inquiéter tant que vous n'avez pas défini de profil.
Doval
1
@Doval - Je conviens que c'est subjectif, c'est pourquoi OP ne devrait pas ignorer entièrement la réponse de npinti, car cela pourrait bien être la meilleure solution. Ce que je voulais dire, c'est que lancer des exceptions n'est pas toujours la meilleure voie à suivre. Il existe de nombreux cas où un échec ne justifie pas le lancement d'une exception et le blocage potentiel d'une application. Par exemple, si la DoStuff()méthode d'OP est nettoyée après que le code interne a généré une exception et que les conditions postérieures de la méthode sont toujours correctes, un code de retour peut être une meilleure option car l'erreur a été gérée.
Karl Nicoll
7

DoStuff() est suffisant, et la valeur de retour de la fonction doit être documentée, et vous n'avez pas besoin de le mentionner dans le nom de la fonction, recherchez plusieurs API disponibles:

PHP

// this method update and return the number affected rows 
// called update instead of updateAndGetAffectedCount
$affected = $query->update(/* values */);

C-Sharp

// Remove item from the List
// returns true if item is successfully removed; otherwise, false.
public bool Remove( T item )
amd
la source
6

En Java, l' API Collection définit une méthode d'ajout qui renvoie un booléen. Il retourne essentiellement si add a changé la collection. Ainsi, pour un Listélément, il retourne généralement la valeur true, car l'élément a été ajouté, alors que pour un, Setil peut renvoyer la valeur false, si l'élément était déjà présent, car Sets autorise chaque élément unique au plus une fois.

Cela dit, si vous ajoutez un élément non autorisé par la collection, par exemple une valeur null, l’ajout jettera un NullPointerException plutôt que de renvoyer faux.

Lorsque vous appliquez la même logique à votre cas, pourquoi devez-vous renvoyer un booléen? S'il s'agit de cacher une exception, ne le faites pas. Il suffit de lancer l'exception. De cette façon, vous pouvez voir ce qui a mal tourné. Si vous avez besoin d'un booléen, parce que cela n'a peut-être pas été fait, pour une raison autre qu'une exception, nommez la méthode DoStuff(), car cela représente le comportement. Ça fait des trucs.

mrjink
la source
1

Pourrait échouer pour différentes raisons, exception, conditions normales, utiliserait donc ceci:

boolean doStuff() - returns true when successful

L'important est de documenter ce que le booléen signifie.

utilisateur136354
la source
1

J'ai l'habitude de méthodes renvoyer un OperationResult(ou OperationResult<T>) et définir la IsSuccesspropriété sur le OperationResultapproprié. Si la méthode 'habituelle' est vide OperationResult, renvoyer , si la méthode 'habituelle' renvoie un objet, renvoyer OperationResult<T>et définir la Itempropriété de OperationResultmanière appropriée. Je suggère également d'avoir des méthodes OperationResulttelles que .Failed(string reason)et .Succeeded(T item). OperationResultdevient rapidement un type familier dans vos systèmes et les développeurs doivent savoir comment le gérer.

Scott Rickman
la source
0

Cela dépend vraiment de la langue que vous utilisez. Comme pour certaines langues, il existe des conventions et des protocoles qui n'existent pas vraiment dans beaucoup de langues.

Par exemple, dans le langage Objective-C et dans le SDK iOS / Mac OS X, les noms de méthode sont généralement les suivants:

- (returndatatype) viewDidLoad {
- (returndatatype) isVisible {
- (returndatatype) appendMessage:datatype variablename with:datatype variablename {

Comme vous pouvez le constater, il existe une méthode appelée viewDidLoad. Je préfère utiliser ce type de nommage chaque fois que je crée mes propres applications. Cela pourrait être utilisé pour vérifier si quelque chose était supposé se passer comme vous l'avez dit. Je pense que vous réfléchissez trop à ce que vous nommez vos méthodes. La plupart des méthodes renvoient le statut de ce qu'elles font, peu importe, prenez l'exemple suivant:

En PHP, mysql_connect () retournera false si la connexion échoue. Il ne dit pas qu'il le fera, mais il le fait et la documentation l'indique, de sorte qu'il ne puisse pas être mal interprété par le programmeur.

Gergy008
la source
Ah, mais viewDidLoad est plus une notification de rappel. Les utilisateurs ne l'appellent pas pour charger la vue. Au lieu de cela, il est appelé après le chargement de la vue. De même: isVisible ne définit pas la visibilité, mais renvoie simplement la valeur actuelle. Ça ne fait rien.
Lilbyrdie
0

J'aimerais suggérer d'utiliser le préfixe "Essayer". Il s'agit d'un modèle bien connu pour les situations dans lesquelles la méthode peut réussir ou non. Ce modèle de dénomination a été utilisé par Microsoft dans .NET Framework (par exemple, TryParseInt).

Mais, peut-être que ce n'est pas à propos de nommer. Il s'agit de la façon dont vous concevez les paramètres d'entrée / sortie de votre méthode.

Mon idée est que si une méthode a une valeur de retour, l'appel de cette méthode devrait alors avoir pour but d'obtenir cette valeur de retour. Donc, vous devriez éviter d'utiliser le type de retour pour indiquer le statut d'une opération.

En C #, je préfère utiliser un paramètre out. En utilisant un out, je marque le paramètre comme paramètre latéral. Spécialement, ce C # 6 supportera la déclaration de variable inline.

DoStuff(out var isStuffDone); // The out parameter name clearly states its purpose.
DoStuff(out var _); // I use _ for naming parameters that I am ignoring.
000
la source
0

Je choisirais un nom basé sur le rôle principal de la méthode. Le fait que cette méthode retourne une valeur booléenne ne requiert pas le préfixe 'Is'. Ayons du code Java, qui utilise le préfixe 'Is' assez souvent. Regarde java.io.File.delete(). Ça revient boolean, mais ça ne s'appelle pas isFileDeleted(). La raison en est que le rôle principal du métod est de supprimer un fichier. Quelqu'un appelle cette méthode avec l'intention de supprimer un fichier.

Le booléen est juste là pour communiquer le résultat du travail. Par contre voyons java.util.Map.isEmpty(). Évidemment, vous appelez une telle méthode pour savoir si la collection est vide. Vous ne voulez rien faire. Vous voulez juste savoir quel est l'état actuel.

Donc, personnellement, je resterais définitivement avec DoStuff().

C'est un problème très important pour moi. Souvent, lorsque je maintiens un code hérité, je tombe sur une méthode que j'appelle personnellement "méthode du mensonge". Le nom suggère l'action A, mais le corps effectue l'action B. L'exemple le plus intéressant est une méthode de lecture qui modifie les données de la base de données.

Jacek Prucia
la source