Question

Permettez-moi de commencer par dire que je ne préconise pas cette approche, mais je l’ai vue récemment et je me demandais s’il y avait un nom pour cela que je pourrais utiliser pour désigner le coupable. Alors voilà.

Vous avez maintenant une méthode et vous souhaitez renvoyer une valeur. Vous souhaitez également renvoyer un code d'erreur. Bien entendu, les exceptions constituent un bien meilleur choix, mais pour une raison quelconque, vous souhaitez plutôt un code d'erreur. Rappelez-vous, je me fais l'avocat du diable ici. Donc, vous créez une classe générique, comme ceci:

class FunctionResult<T>
{
    public T payload;
    public int result;
}

Et déclarez ensuite vos fonctions comme suit:

FunctionResult<string> MyFunction()
{
    FunctionResult<string> result;
    //...

    return result;
}

Une variante de ce modèle consiste à utiliser une énumération pour le code d'erreur au lieu d'une chaîne. Maintenant, revenons à ma question: y a-t-il un nom pour cela, et si oui de quoi s'agit-il?

Était-ce utile?

La solution

Je conviens que ce n'est pas spécifiquement un anti-modèle. Cela peut être une odeur en fonction de l'utilisation. Il y a des raisons pour lesquelles on ne souhaite pas réellement utiliser d'exceptions (par exemple, les erreurs renvoyées ne sont pas "exceptionnelles", pour commencer).

Dans certains cas, vous souhaitez qu'un service renvoie un modèle commun pour ses résultats, comprenant à la fois des erreurs et des valeurs correctes. Cela peut être encapsulé par une interaction de service de bas niveau qui traduit le résultat en une exception ou une autre structure d'erreur, mais au niveau du service, le service retourne un résultat et un code d'état sans avoir à définir une structure d'exception pouvant doivent être traduits à travers une limite distante.

Ce code peut ne pas être nécessairement une erreur non plus: considérez une réponse HTTP, qui consiste en une multitude de données différentes, y compris un code d'état, ainsi que le corps de la réponse.

Autres conseils

Eh bien, ce n'est pas un anti-modèle. La bibliothèque standard C ++ utilise cette fonctionnalité et .NET propose même une classe spéciale FunctionResult dans le framework .NET. Cela s'appelle Nullable . Oui, ceci n'est pas limité aux résultats fonctionnels, mais il peut être utilisé pour de tels cas et est en réalité très utile ici. Si .NET 1.0 avait déjà la classe Nullable , il aurait certainement été utilisé pour les méthodes NumberType.TryParse au lieu du paramètre out . .

Je passe généralement la charge utile en tant que référence (non constante) et le code d'erreur en tant que valeur de retour.

Je suis un développeur de jeux, nous bannissons les exceptions

Konrad a raison, C # utilise des valeurs de retour doubles tout le temps. Mais j'aime bien les méthodes TryParse, Dictionary.TryGetValue, etc. en C #.

int value;
if (int.TryParse("123", out value)) {
    // use value
}

au lieu de

int? value = int.TryParse("123");
if (value != null) {
    // use value
}

... principalement parce que le modèle Nullable ne s'adapte pas aux types de retour non-Value (c'est-à-dire, instances de classe). Cela ne fonctionnerait pas avec Dictionary.TryGetValue (). Et TryGetValue est à la fois plus agréable qu'un KeyNotFoundException (aucune "exception de la première chance" constamment dans le débogueur, sans doute plus efficace), plus agréable que la pratique de get () renvoyant la valeur null (et si des valeurs nulles sont attendues), et plus efficace que d'avoir appeler d'abord ContainsKey ().

Mais ceci est toujours un peu visqueux - puisque cela ressemble à C #, il devrait utiliser un paramètre out. Tous les gains d’efficacité sont probablement perdus en instanciant la classe.

(Peut-être Java, sauf que le type "chaîne" est en minuscule. En Java, vous devez bien sûr utiliser une classe pour émuler les valeurs de retour doubles.)

Je ne suis pas sûr que ce soit un anti-modèle. J'ai souvent vu cela utilisé à la place d'exceptions pour des raisons de performances, ou peut-être pour rendre plus explicite le fait que la méthode peut échouer. Pour moi, cela semble être une préférence personnelle plutôt qu'un anti-modèle.

Je suis d'accord avec ceux qui disent que ce n'est pas un anti-modèle. C'est un modèle parfaitement valable dans certains contextes. Les exceptions concernent les situations exceptionnelles , les valeurs renvoyées (comme dans votre exemple) doivent être utilisées dans les situations attendues. Certains domaines attendent des résultats de classes valides et non valides, et aucun de ceux-ci ne doit être modélisé comme une exception.

Par exemple, étant donné la quantité d'essence X, une voiture peut-elle aller de A à B, et si oui, combien d'essence reste-t-il? Ce type de question est idéal pour la structure de données que vous avez fournie. On ne s'attend pas à ce que le voyage de A à B soit possible. Par conséquent, aucune exception ne devrait être utilisée.

Cette approche est en réalité bien meilleure que d’autres que j’ai déjà vues. Certaines fonctions en C, par exemple, lorsqu'elles rencontrent une erreur, elles reviennent et semblent réussir. Le seul moyen de savoir qu'ils ont échoué consiste à appeler une fonction qui obtiendra la dernière erreur.

J'ai passé des heures à essayer de déboguer du code de sémaphore sur mon MacBook avant de découvrir que sem_init ne fonctionnait pas sous OSX! Il a compilé sans erreur et a fonctionné sans causer d'erreur - mais le sémaphore ne fonctionnait pas et je ne pouvais pas comprendre pourquoi. Je plains les personnes qui portent une application qui utilise sémaphores POSIX à OSX et doit traiter les problèmes de conflit de ressources qui ont déjà été débogués.

Qu'en est-il de la question "Je ne peux pas décider s'il s'agit d'une erreur ou non" modèle. On dirait que si vous aviez vraiment une exception mais vouliez renvoyer un résultat partiel, vous encapsuleriez le résultat dans l'exception.

Si vous ne souhaitez pas utiliser d'exceptions, la méthode la plus propre consiste à demander à la fonction de renvoyer le code d'erreur / succès et de prendre un argument de référence ou de pointeur à remplir avec le résultat.

Je n'appellerais pas cela un anti-motif. C'est une méthode pratique qui a fait ses preuves et qui est souvent préférable à l'utilisation d'exceptions.

Si vous vous attendez à ce que votre méthode échoue à l'occasion, mais ne considérez pas cela comme exceptionnel, je préfère ce modèle tel qu'il est utilisé dans le .NET Framework:

bool TryMyFunction(out FunctionResult result){    

     //...    
     result = new FunctionResult();
}

Les débats sur les odeurs et les anti-motifs me rappellent le "survivant". Émissions de télévision, où vous avez différentes constructions de programmation en essayant de voter les uns des autres hors de l'île. Je préférerais voir "Construire X présente de tels avantages et inconvénients", plutôt qu'une liste en constante évolution d'édits de ce qui devrait ou ne devrait pas être fait.

Pour la défense de la désignation anti-modèle, ce code se prête à plusieurs utilisations:

  1. Objet x = MyFunction (). payload; (en ignorant le résultat de retour - très mauvais)
  2. int code = MyFunction (). result; (jeter la charge utile - cela peut aller si c'est l'usage prévu.)
  3. FunctionResult x = MyFunction (); // ... (un tas d'objets FunctionResult supplémentaires et un code supplémentaire pour les vérifier partout)

Si vous devez utiliser des codes de retour, c'est bien. Mais utilisez ensuite les codes de retour. N'essayez pas de charger une charge supplémentaire. C’est à cela que servent les paramètres ref et out (C #). Les types Nullables peuvent constituer une exception, mais uniquement parce que le langage utilisé pour le supporter contient plus de sucre que nécessaire.

Si vous n’êtes toujours pas d’accord avec cette évaluation, cochez cette réponse (et non la question dans son ensemble). Si vous pensez que c'est un anti-modèle, alors UPVOTEZ-le. Nous allons utiliser cette réponse pour voir ce que la communauté pense.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top