web-dev-qa-db-fra.com

Quelle exception lever pour une entrée non valide qui est valide du point de vue du client

J'écris du code pour trouver et croiser 2 lignes. Lorsque les pentes des lignes sont égales, elles ne se coupent pas. Mais d'un autre côté, une entrée avec des pentes de valeur égale est complètement valide.

public static Point calculateIntersection(Line line1, Line line2) {

    if (line1 == null || line2 == null) {
        throw new NullPointerException(" some message ");
    }

    if (line1.getConstant() == line2.getConstant()) {
        return new Point(0, line1.getConstant());
    }

    if (line1.getSlope() == line2.getSlope()) {
        throw new IllegalArgumentException("slopes are same, the lines do not intersect.");
    }

    int x = (line2.getConstant() - line1.getConstant()) / (line1.getSlope() - line2.getSlope());
    int y = line1.getSlope() * x + line1.getConstant();

    return new Point(x, y);
}

La question est de jeter l'exception d'argument illégal la bonne chose à faire? Étant donné que la saisie est valide, elle ne me convainc pas complètement.

L'exception personnalisée est-elle la bonne chose à faire? Cela semble être un bon choix, mais une opinion supplémentaire serait utile.

Merci

32
JavaDeveloper

La question est lancer une exception d'argument illégal est-il la bonne chose à faire?

Cela dépend de la façon dont vous voulez/devez "encadrer" cette condition; c'est-à-dire est-ce un bug, une erreur de saisie utilisateur ou quelque chose que le programme est censé pouvoir traiter?

  • Si le cas de deux lignes ne se croisant pas est sans équivoque un "bug", alors IllegalArgumentException est très bien. C'est à cela que l'exception est destinée. (Notez qu'il s'agit d'une exception non vérifiée, donc on s'attend à ce qu'il ne soit pas récupéré/récupéré.)

  • Si vous pensez que le programme pourra récupérer de lui-même, alors une exception personnalisée est la meilleure idée. De cette façon, vous réduisez la possibilité que votre code soit confus en (disons) une méthode de bibliothèque lançant (disons) un IllegalArgumentException ... qui signifie autre chose que "deux lignes intersectées".

  • Si ce cas est quelque chose que vous prévoyez de signaler à l'utilisateur final dans le cadre de la validation d'entrée, une exception générique "erreur de validation" peut être plus appropriée qu'une exception personnalisée spécifique. Cependant, cette méthode ne semble pas avoir été conçue pour être utilisée (uniquement) pour la validation des entrées utilisateur.

35
Stephen C

Cela ne devrait presque certainement pas lever d'exception, car il est parfaitement logique d'invoquer une méthode comme celle-ci avec deux valeurs Line. Vous avez déjà correctement géré les valeurs nulles.

Vous avez également, très raisonnablement, défini le comportement de votre classe dans l'une des situations d'entrée mal définies, à savoir deux lignes coïncidentes "constantes" (horizontales), où vous renvoyez le point à x=0 sur cette ligne. Vous devez également sélectionner des valeurs de retour pour les autres cas d'entrées mal définies: lignes verticales coïncidentes, lignes coïncidentes qui ne sont ni horizontales ni verticales et lignes parallèles non coïncidentes.

À mon avis, le résultat le plus naturel pour le dernier cas - lignes parallèles non coïncidentes - serait null, reflétant le fait qu'il n'y a pas de point d'intersection.

Il appartiendrait alors au client de décider si une intersection nulle justifie une exception, un message d'erreur ou autre. Par exemple. un shell interactif invitant l'utilisateur à intersecter des lignes afficherait probablement un message d'erreur et demanderait à l'utilisateur de réessayer. Certains calculs plus compliqués, par exemple un optimiseur linéaire essayant de définir des limites pour sa recherche, pourrait vouloir lancer IllegalArgumentException si les contraintes donnant naissance aux lignes parallèles se contredisent.

Bien sûr, les valeurs de retour dans tous ces cas (lignes coïncidentes ou lignes parallèles non coïncidentes) doivent être précisément documentées dans le javadoc de la méthode.

4
Andy Lowry

Je dirais que vous faites la bonne chose: vous attrapez la maladie tôt. C'est soit cela, soit les gens se plaindront que "votre programme est bogué, regardez ces données d'entrée, division par 0".

Étant donné qu'il n'y aura pas une telle erreur dans 99 +% des situations, il s'agit d'une condition exceptionnelle et ne permet pas de déclarer une exception vérifiée, donc une exception non vérifiée ressemble en effet au bon choix.

Maintenant, quant à savoir si IllegalArgumentException est le "bon", c'est au moins l'exception la plus proche décrivant ce cas ... Vous pouvez, si vous sentez que vous avez un meilleur nom, toujours créer votre propre héritant RuntimeException.

SI , d'autre part, cette situation n'est pas si rare, alors peut-être que la logique pour atteindre cette fonction devrait être revue afin de ne jamais atteindre cette condition dans la première place.

2
fge
1
arcadeblast77

Comme le disent @ Andy-Lowry et @KamikazeCZ, cela ne devrait pas faire exception.

Cette méthode ne devrait pas se préoccuper de savoir si le client s'attend à ce que les lignes se coupent toujours ou non; il ne devrait se soucier que de trouver l'intersection de deux lignes - ce qui, par nature, pourrait ne pas arriver.

Si l'appelant obtient un résultat indiquant qu'il n'y a pas d'intersection, alors CE code peut décider s'il s'agit d'une entrée non valide parce que l'utilisateur final a été dûment averti, ou quelque chose qu'il peut gérer (peut-être par une nouvelle invite), ou lever une exception personnalisée.

Donc, revenons à ce que devrait cette méthode retourne? Une sorte de valeur sentinelle, de la même manière que indexOf renvoie -1 dans la bibliothèque de collections. Renvoyer null est une sentinelle raisonnable. Dans Java 8, vous pouvez renvoyer un Optional<Point>, Pour aider à rappeler à l'appelant qu'il pourrait ne pas y avoir de Point approprié.

Vous avez aussi un problème supplémentaire: que demande quelqu'un pour l'intersection d'une ligne avec lui-même? (Mathématiquement, l'intersection de deux lignes est soit 0 point, 1 point ou infiniment de points.) Vous devrez peut-être retourner deux valeurs sentinelles, qui sont plus impliquées, en Java. Cette méthode pourrait sortir de la situation cette fois, en disant "dans le cas de réponses multiples, cette méthode peut renvoyer n'importe laquelle", ou (ce que je ferais probablement) "... retourne le point le plus proche de l'origine" .

En fait, cette pensée découle en grande partie d'un état d'esprit de test unitaire: commencez par définir la bonne réponse devrait être pour une variété d'entrées de cas de coin, avant de se lancer dans le code et de vous engager un peu sur un certain type de retour, etc.

Enfin: lorsque vous comparez les résultats de getSlope() à l'aide de ==, Méfiez-vous des erreurs d'arrondi à virgule flottante. C'est peut-être la meilleure chose à faire ici, mais c'est toujours problématique. La façon dont vous supposez (ou arrondissez) l'intersection avec ints, cependant, suggère que vous pourriez avoir des contraintes/hypothèses très spéciales dans votre problème.

1
not-just-yeti