Venant d'un monde PHP où il n'y a qu'une seule façon d'écrire la gestion des exceptions .. J'ai trouvé l'encapsulation des exceptions en Java un peu "moche":

public void exampleOneException(String input) throws MyBusinessException {
    try {
        // do something
    } catch (NumberFormatException e) {
        throw new MyBusinessException("Error...", e);
    }
}

Je préfère utiliser ce style à la place:

public void exampleTwoException() {
    try {
        // do something
    } catch (MyBusinessException e) {
        log.error("Error...: " + e);
    } catch (NumberFormatException e) {
        log.error("Error...: " + e);
    }
}

Existe-t-il des différences ou des meilleures pratiques concernant ces approches de gestion des exceptions?

7
numediaweb 16 nov. 2017 à 11:51

4 réponses

Meilleure réponse

Ce sont deux approches valables pour deux scénarios différents.

Dans le premier scénario, la méthode ne peut rien faire d'intelligent sur l'exception, mais elle doit la «signaler» vers le haut. De cette façon, l'appelant peut attraper l'exception et décider quoi faire avec elle (par exemple, annuler le flux, afficher un message à l'utilisateur, le consigner, etc.)

Dans le deuxième scénario, vous interceptez l'exception et la consignez, masquant efficacement l'échec de l'appelant. Ce type de traitement peut être utile si l'appelant ne se soucie pas vraiment de savoir si l'opération a réussi ou non.

7
Mureinik 16 nov. 2017 à 08:54

Du point de vue d'un code clair et solide, vous n'allez pas dans la bonne direction.
Premièrement, vous n'avez pas besoin d'utiliser try / catch pour un Exception qui est déjà lancé par l'en-tête de la méthode. Donc, dans votre premier cas, le code devrait être comme ci-dessous:

 public void exampleOneException(String input) throws MyBusinessException {
    // do something
 }

C'est plus joli n'est-ce pas? Dans le cas ci-dessus, si le MyBusinessException se produit alors l'exception sera avalée de sorte que l'utilisateur ne verra jamais ce qui s'est passé. Si vous voulez que cela se produise, c'est votre meilleure pratique.

Le deuxième cas est clair. Vous détectez deux exceptions différentes et vous les gérez (par journalisation), ce qui est une meilleure pratique en général.

Ainsi, la meilleure pratique de vos deux cas différents dépend de ce que vous voulez réaliser. Concernant la beauté, c'est totalement subjectif.

0
0__1 16 nov. 2017 à 09:46

Je dirais que NumberFormatException et MyBusinessException sont utiles mais dans des cas différents.

Ils apparaissent généralement à différents niveaux de la hiérarchie des classes: par exemple NumberFormatException est une exception de niveau inférieur et vous pourriez ne pas vouloir l'exposer à un niveau supérieur (par exemple, interface utilisateur) si l'utilisateur de celui-ci n'a pas le pouvoir de récupérer à partir de cela. Dans ce cas, il est plus élégant de lancer simplement MyBusinessException et d'afficher un message informatif qui explique par exemple que quelque chose dans une étape précédente a été mal fourni ou qu'une erreur de traitement interne s'est produite et qu'il / elle doit redémarrer le processus.

D'un autre côté, si votre fonction est utilisée à un niveau intermédiaire (par exemple API) et que le développeur a les moyens de se remettre du comportement exceptionnel, NumberFormatException est plus utile, car il peut être traité par programme et le le flux de l'application peut continuer avec une interruption minimale (par exemple, fournir un numéro valide par défaut). Cela peut également indiquer une faille / un bogue dans le code qui devrait être corrigé.

Pour plus d'informations sur la manière de suivre les meilleures pratiques en matière d'utilisation des exceptions, consultez Item 61 - Throw exceptions appropriate to the abstraction from Effective Java par Joshua Bloch.

1
aUserHimself 17 nov. 2017 à 07:59

Le premier exemple serait généralement considéré comme la meilleure approche.

Vous ne devez pas non plus considérer le MyBusinessException comme enveloppant le NumberFormatException, mais plutôt le NumberFormatException est la cause du {{ X3}}.

Les exceptions doivent être appropriées pour l'interface exposée. Un appelant de votre interface ne devrait pas avoir besoin de connaître ou de traiter les détails d'implémentation. À moins qu'un NumberFormatException ait vraiment un sens comme type d'erreur lors de l'appel de exampleOneException, il devrait être traduit en une exception plus appropriée.

Un exemple plus concret comprend généralement des implémentations différentes, où les utilisateurs de l'interface ne devraient pas être tenus de traiter les spécificités de l'implémentation (qui peuvent même ne pas être connues au moment de la compilation).

interface MyRepository {
    Object read(int id) throws ObjectNotFoundException;
}

// a sql backed repository
class JdbcRepository implements MyRepository {
    public Object read(int id) throws ObjectNotFoundException {
        try { ... }
        catch (SQLException ex) {
            throw new ObjectNotFoundException(ex);
        }
    }
 }

// a file backed repository
class FileRepository implements MyRepository {
    public Object read(int id) throws ObjectNotFoundException {
        try { ... }
        catch (FileNotFoundException ex) {
            throw new ObjectNotFoundException(ex)
        }
    }
}

Étant donné que l'interface déclare les types d'erreurs qu'elle peut renvoyer, les clients de cette interface peuvent être cohérents et rationnels. Ajouter du code pour gérer FileNotFoundException et SQLException alors l'implémentation réelle peut être l'un ou l'autre n'est pas fantastique.

Considérez s'il y avait plusieurs endroits dans l'implémentation de FileRepository qui pourraient générer FileNotFoundException. Cela implique-t-il que chacun d'eux implique un objet introuvable ?

Lorsque vous envisagez l'option deux, exampleTwoException, il est important de réaliser que votre bloc catch indique effectivement que les effets de toute erreur survenue ont été atténués. Il existe des cas où les exceptions vérifiées sont raisonnablement ignorées, mais il est bien plus probable qu’une simple

try { ... }
catch (SomeException ex) {
    log.error("caught some exception", ex);
}

Est en fait le résultat d'un développeur ne tenant pas compte des conséquences de l'exception, ou le code aurait dû inclure un FIXME.

Cela est encore plus vrai lorsque vous voyez catch (Exception ex), ou l'injure catch (Throwable ex).

Et enfin, voudriez-vous être la personne qui fouille dans une application pour trouver tous les endroits où vous devez ajouter (encore) un autre bloc catch pour gérer une nouvelle implémentation? C'est peut-être une cause de catch (Exception ex) ...

Toujours lancer des exceptions appropriées à l'abstraction

3
ptomli 16 nov. 2017 à 09:29
47325247