Archive

Posts Tagged ‘style’

Gestion des exceptions en java…

Je déteste le système des exceptions en java, je le trouve lourd et inadapté aux problèmes de la vie réelle : aprés tout, si on développe un programme, c’est pour qu’il fasse des trucs, et par pour qu’il gère des exceptions ! Le paradoxe c’est que si on s’y prend mal, on finit par avoir du code source qui passe plus de temps a gérer des try/catch qu’a faire de vraies choses !

Voici un mémo que j’ai écrit il y a déja un certain temps concernant mon point de vue sur toutes ces questions :

En Java, une distinction est faite entre :

  • les exceptions déclarées dans la signature d’une méthode (CheckedException en Java) ; par exemple
   void foo () throws ThisExceptionType { ... },
  • les exceptions à l’exécution (RuntimeException en Java), qui correspondent à des évènements impossibles à localiser lexicalement à la compilation (les exceptions asynchrones), ou pouvant survenir à tout moment dans l’exécution du programme, comme les problèmes d’allocation mémoire. Il n’est pas nécessaire de déclarer ces exceptions (par exemple, NullPointerException, OutOfMemoryException etc…)

Les « checked exceptions » (i.e les exceptions qui héritent de java.lang.Exception) essayent de résoudre un problème de contrat. L’interface d’un module (d’une bibliothèque de classes) représente un contrat entre l’auteur du module et son utilisateur : l’argument est qu’un tel contrat ne devrait pas passer sous silence les exceptions susceptibles d’être propagées hors des frontières du module.

En spécifiant les exceptions dans les signatures des méthodes, on introduit toutefois un problème. En effet, les méthodes clientes doivent choisir dans l’alternative :

  • installer un gestionnaire d’exception (bloc try/catch pour les exceptions du module, ou bien
  • déclarer à leur tour qu’elles sont susceptible de lancer ces exceptions.

Les méthodes utilisant des checked exceptions contaminent leurs clients avec l’obligation de décorer leur signature s’ils n’installent pas de gestionnaire pour ces exceptions. Cette contamination trahit en partie le contrat d’indépendance entre le lieu du signalement d’une exception et le lieu de son traitement, en exposant toutes ces déclarations d’interfaces dans le chemin d’appel ; en somme elles nous ramènent aux inconvénients des langages de programmation sans SGE (transmission de l’exception par une valeur de retour spéciale, prévue en tout point de la chaîne d’appels). A l’opposé Les « checked exceptions » violent finalement la philosophie des exceptions (la non-localité entre le lieu du signalement du problème et le lieu de son traitement).

Du coup, la bibliothèque standard de Java utilise en pratique des runtime exceptions pour les opérations les plus triviales (arithmétique, collections, allocation mémoire) afin éviter la pollution lexicale des checked exception.

Donc en résumé :

  • Si on déclare ses méthodes « throws … », on force l’utilisateur a gérer l’erreur, ce qui alourdit le code, (multiplication des type d’exception a déclarer, catcher, etc)
  • Si on lance une exception non checkée, on risque de lancer des erreurs qui ne seront pas traitées correctement, mais on rend l’api plus simple.
  • Une exception checkée est plutot causée par une erreur de contexte (fichier manquant, imprimante pas prète, etc..) -> elle se résoud en changeant l’environnement d’éxecution du programme.
  • Une exception non checkée est plutot causée par une erreur de programmation pointeur null, division par zero, threads pas synchronisés, etc… -> Elle se résoud en corrigeant son programme.

Ce que j’ai vu :

throws Exception (ou « ma méthode plante des fois »)

Certains d’entre nous déclarent leurs méthodes comme « throws Exception », ce qui sémantiquement NE VEUT RIEN DIRE, on oblige juste l’utilisateur de sa méthode à l’encadrer d’un bloc try/catch, qui ne pourra rien faire d’intéressant si on ne connait pas la nature de l’erreur.. donc :

  • regle 1 :’ bannir throws Exception, on a le droit de lancer des exceptions non checkées

des print dans les catch

   catch(Exception e)
   {
      e.printStackTrace();
   }
  • 1) la console n’est pas une poubelle. La tradition veut qu’on utilise la sortie d’erreur standard d’un programme pour tracer les erreurs : Donc dans l’absolu :
e.printStackTrace(System.err)

Mais cela n’est pas trés important puisque :

  • 2) De toute façon, personne ne lit la console, surtout quand l’application est hébergée sur un serveur ! Donc utiliser un service de log (log4j) – Rechercher systématiquement les println dans le code source en fin de développement, afin de les éradiquer.
  • 3) faire juste un print n’est pas un traitement d’exception, c’est un constat d’impuissance. Si l’erreur va provoquer un plantage ailleurs, ou si tout simplement la méthode ne fera pas ce qui lui a été demandé a cause de cette erreur, il faut relancer une exception !! Soit en la transformant en une autre exception lancée explicitement par la méthode courante, Soit en relancant une exception non checkée (new RuntimeException(e), new IllegalArgumentException(e), NotImplementedException, etc.. etc…) Soit en ne la catchant pas !!!
  • 4) Si gestionnaire d’exception il y a (pour informer l’utilisateur, par exemple), il doit pouvoir être en mesure d’attraper le plus d’exceptions possibles, donc éviter de réinventer la roue carrée a longueur de temps ! par la peine de se fatiguer a faire de « catch print » partout, si de toute façon l’erreur pourrait être mieux gérée en amont par du code écrit exprès !

Comment je vois les choses :

Une solution en valant une autre, j’ai décidé de m’appliquer des conventions dans mon style de codage, et je vous invite a faire comme moi !

  • REGLE 1 : throws Exception n’est pas mon ami – éviter les throws n’importe quoi
  • REGLE 1 : Lancer des exceptions non checkée n’est pas un crime, surtout quand l’erreur est une erreur de programmation, (et pas de runtime) – si il ne tenait qu’a moi, la plupart des exceptions devraient être unchecked.
  • REGLE 3 : pas de gestionnaires sauvages, pas de gestionnaires longs (pas plus de 3 lignes), pas de gestionnaire qui font des trucs « métier », en particulier pas de print, pas d’envois d’email dans un catch (comme je l’ai déja vu…), etc…
  • REGLE 4 : Relancer une erreur (quitte a la rendre unchecked) plutot que de la gérer mal (en faisant un print)
/** Service "clients" - manipulation de l'entité "client" */
public class ClientServiceImpl extends JpaDaoSupport implements ClientService {

    /**
      *  Obtenir un client en fct de son id
      *  @param id un code client
      *  @return une instance de Client, ou null si pas trouvé.
      *  @throws RuntimeException quand une erreur empèche l'accés au données.
      */
    @Override
    @CacheResults(cache="clientServiceCache", maxAge=2000)
    public Client getClientById(int id)
    {
        try
        {
            return getJpaTemplate().find(Client.class,id);
        }
        catch(DataAccessException e)
        {
            //getLogger().error(e);//optionellement, on loggue, mais ça n'est vraiment pas obligatoire
            throw new RuntimeException(e);
        }
    }
}
Publicités
Catégories :Uncategorized Étiquettes : , , ,