Exception et méthodologie ...

Signaler
Messages postés
514
Date d'inscription
mercredi 19 mars 2003
Statut
Membre
Dernière intervention
1 mars 2009
-
Messages postés
2483
Date d'inscription
jeudi 30 novembre 2006
Statut
Membre
Dernière intervention
14 janvier 2011
-
    Salut à tous,
    Alors voilà ... Une petite question qui parrait un peu inutile, mais c'est une question qui me taraude depuis pas mal de temps et je sais pas trop quelle est la réponse ... On va voir ce que vous en pensez !

    Imaginons que l'on a une classe Etudiant qui possède la méthode suivante veutRecevoirCourriel(), qui retourne true s'il veut recevoir automatiquement des messages provenant de mon site et false dans le cas contraire, ainsi que la méthode suivante envoyerCourriel() qui envoie un mail à l'étudiant.
Imaginons maintenant que l'on possède un tableau qui contient une liste d'instance de cette classe Etudiante et que l'on veuille écrire un script qui automatise l'envoie d'une lettre d'information à tous ces étudiants. Je vois alors 2 solutions : l'une sans les exceptions, la plus commune dirais-je :

foreach($arrEtudiants as $objEtudiant) {
    if ($objEtudiant -> veutRecevoirCourriel()) {
       try {
          $objEtudiant -> envoyerCourriel();
       } catch (ErreurEnvoiCourriel $objErreur) {
          echo 'L'envoi du mail à échoué ...';
       }
    } else {
       echo 'L'étudiant ne veut pas recevoir de mail ...';
    }
}

    Mais en fait, je me dit que l'on pourrait faire autrement, en utilisant les exceptions. Imaginons que si l'on utilise envoyerCourriel() et que l'étudiant ne veut pas recevoir de mail, alors l'exception NeVeutPasRecevoirCourriel est levée ... Ne pourrait-on pas alors faire :

foreach($arrEtudiant as $objEtudiant) {
    try {
       $objEtudiant -> envoyerCourriel();
    } catch (NeVeutPasRecevoirCourriel $objException) {
       echo 'L'étudiant ne veut pas recevoir de mail ...';
    } catch (ErreurEnvoiCourriel $objErreur) {
       echo 'L'envoi du mail à échoué ...';
    }
}

    Au final, ces 2 scripts reviennent au même ... Mais d'un point de vue méthode, lequelle est le mieux ?
    Merci pour vos commentaires !

        LocalStone

8 réponses

Messages postés
12303
Date d'inscription
mardi 10 février 2004
Statut
Modérateur
Dernière intervention
30 juillet 2012
42
Salut
Perso je prefere ta seconde methode, elle semble meilleur car quand tu appelles la methode envoyer sans verifier :  veutRecevoirCourriel() alors tu enveras un courier a quelqu'un qui n'en veut pas...

La seconde methode semble tout aussi souple, plus concise, plus claire, bref, meilleur selon moi
Messages postés
10839
Date d'inscription
lundi 24 février 2003
Statut
Modérateur
Dernière intervention
2 mars 2010
25
Hello,

c'est une question de philosophie.
Moi la 2de me parait bizarre en fait. Le fait de vouloir ou de ne pas vouloir recevoir un email n'est pas une erreur de fonctionnement de ton application ou de son utilisation.
Mais pourquoi pas, j'utilise aussi parfois les exceptions cpomme des erreurs "utilisateur" : on a un pavé de login, le mec rentre son identifiant et son mot de passe. Jé vérifie en base : si je ne trouve pas son couple identifiant/mot de passe, je lance une exception "Identification échouée" et évidemment, c'est ce que je vais affiocher à mon utilisateur. Le script s'arrête alors : normal ici, pour continuer, il DOIT s'identifier.
Mais dans ton cas, avec ta 2de méthode, tu t'arrêtes de tte manière dans chaque catch : tu envoie l'email ou pas, et basta, exécution terminée (enfin, tout ce que tu mets dans tes catch sera exécuté). Si c'est normal pour le 2d catch, ça l'est moins pour le 1er à mon sens.
Mais ça reste juste une question de philosophie.
Moi, je pense que j'appelerais etudiant::veutRecevoirCourriel() dans etudiant::envoyerCourriel(), sans lancer d'exception si l'étudiant ne veut pas recevoir son email. etudiant::envoyerCourriel() se chargerait de vérifier si oui ou non il doit envoyer l'email, et basta (et lancer son exception si il y a erreur d'envoi, évidemment). Plus un petit observateur pour comptabiliser les emails effectivement envoyés :-)
Messages postés
2483
Date d'inscription
jeudi 30 novembre 2006
Statut
Membre
Dernière intervention
14 janvier 2011
18
Salut,

Je sais pas si ma réponse sera utile ni même pertinente dans le contexte présent... Je balance quand même.

Je me suis beaucoup interrogé sur la gestion des erreurs. Quand utiliser une exception, quand utiliser une erreur (trigger_error), ou pourquoi ne pas utiliser son propre système d'erreurs ?

A force de lectures sur le net, notamment dernièrement un tuto de Marcus Börger de PHP Québec, qui fait l'apologie de la SPL et qui aborde la question des exceptions (la SPL introduisant quelques exceptions étendues qu'il est bon d'utiliser à bon escient), je sais (ou pense savoir, peu importe) quand utiliser quoi.

Les exceptions doivent être utilisées quand le script DOIT s'arrêter, quand une véritable erreur est survenue (éventuellement quand une condition indispensable au déroulement du script n'est pas satisfaite).

Les autres erreurs, quand elles sont gérées par un gestionnaire approprié peuvent servir à peu près à tout ce qu'on veut : erreur de connexion (ça n'empêche pas la page de s'afficher normalement avec un message expliquant qu'il faut saisir un login), argument non valide passé dans l'url (on affiche la page demandée avec des valeurs par défaut, ou au pire, une 404).

Par exemple, l'échec de la connexion à la base de données ne nécessite pas forcément l'envoi d'une exception, notamment si le site peut utiliser un système de cache pour afficher malgré tout les pages demandées (dans ce cas, pas de connexion membre, pas de stats des connectés, etc). On peut lever une exception si on n'a pas de gestionnaire de cache (ce qui est le plus courant).

Au final, je rejoins malalam sur ce point : ça reste une question de philosophie, ça dépend vraiment du contexte, du script, de l'application, du site, du fonctionnement général, mais aussi de ce avec quoi on est le plus à l'aise.
Messages postés
514
Date d'inscription
mercredi 19 mars 2003
Statut
Membre
Dernière intervention
1 mars 2009

Merci beaucoup pour vos réactions !
Pour reprendre la phrase de Malalam, c'est vrai que c'est une question de philosphie
Mais en fait, y a une phrase de Neigedhiver qui m'a fait réagir. D'après toi, les exceptions doivent être utilisés quand le script doit s'arrêter, lorsqu'une erreur est survenue ... A ce compte là, je ne comprends pas trop l'interêt des exception. En effet, si le script doit s'arrêter, autant faire un bon vieux die(), non ? Parce qu'après tout, le fait d'utiliser une clause try catch, ça empêche justement la propagation de l'exception et de ce fait l'arrêt du script.
Et du coup, une autre question me vient ... Pensez-vous qu'il soit judicieux d'utiliser les try catch avec nos propres gestionnaires d'evenements ? Pour reprendre l'exemple de cache et de base de données proposé par Neigedhiver ... Imaginons deux classes Alerte et Erreur qui dérivent toutes deux de la classe Exception ... On pourrait très bien lancer une exception lorsque la base de données n'est pas joignable, et du coup, dans le catch, mettre en place le système de cache ...

LocalStone
Messages postés
2483
Date d'inscription
jeudi 30 novembre 2006
Statut
Membre
Dernière intervention
14 janvier 2011
18
Salut,

La différence avec le "bon vieux die()", c'est qu'il n'en existe qu'un. Des exceptions, il en existe virtuellement une infinité. Ce qui permet de créer et d'intercepter une infinité d'erreurs d'autant de types différents. On peut effectuer un traitement identique. Ca simplifie énormément le travail et rend le code beaucoup plus lisible. Les exceptions font partie intégrante de la POO, du fait que ce sont des objets, mais aussi parce que d'un point de vue plus analytique, elles permettent de réutiliser du code tout en séparant les tâches. Ca contribue à faciliter la maintenance et la portabilité de classes de manière indépendante.

Mon exemple de cache et de base de données ne t'a visiblement pas laissé indifférent. J'en suis bien content :)
Effectivement, dans la théorie, rien n'empêche d'afficher une page en cache depuis le bloc catch. C'est une manière de gérer l'erreur. Encore une fois, c'est une question de philosophie.

Ce qui est important, selon moi, c'est que le déroulement normal du script doit se trouver dans le bloc try. Le bloc catch n'est là que pour intercepter l'erreur. Si le traitement que l'on fait dans le bloc catch nécessite un traitement "normal", alors il n'a, à mon sens, pas sa place dans le bloc catch, et l'erreur qui a conduit ici devrait ne pas lever d'exception ni d'erreur.

J'ai retrouvé le lien où télécharger ce document dont je parlais, réalisé par Marcus Börger : http://talks.somabo.de/#20050401

L'auteur dit clairement :
" Respect theses rules :
1. Exceptions are exceptions
2. Never use exceptions for control flow
3. Never use exceptions for parameters passing"

On est en droit de considérer que le fait que la base de données ne soit pas joignable est exceptionnel. La règle 1 est donc bien respectée.
Par contre, la règle 2 est un peu plus litigieuse... Dans quelle mesure, dans le cas qui nous préoccupe, est-ce qu'on n'utilise pas l'exception pour effectuer un traitement différent ? Si on prévoit un système de cache pour fonctionner sans base de données, on prévoit un fonctionnement normal dans le cas où il n'y a pas de base de données, même si ce cas en lui-même n'est pas normal... Je sais pas si c'est clair...
Le fait de prévoir un traitement spécifique implique qu'on l'intègre dans le fonctionnement normal de son application. Il n'est pas logique d'utiliser alors une exception pour choisir le traitement que l'on va faire.

Une exception doit être levée quand on ne sait pas quoi faire, ou qu'on ne peut rien faire d'autre que d'interrompre le script avec un message d'erreur.
Messages postés
12303
Date d'inscription
mardi 10 février 2004
Statut
Modérateur
Dernière intervention
30 juillet 2012
42
faut voir qui leve l'exception aussi... en POO t'as des boites elles ont des limites ton systeme de gestion de base de donnee il s'en tape qu'il y ai un systeme de cache a cote, c'est une autre boite....

ce que je veux dire c'est que pour elle, ne pas fonctionner ce n'est pas normal, c'est une exception, elle est donc en droit d'en lever une...
Messages postés
2483
Date d'inscription
jeudi 30 novembre 2006
Statut
Membre
Dernière intervention
14 janvier 2011
18
Mince, ballot qu'on puisse pas éditer ses messages...

Tu dis : "après tout, le fait d'utiliser une clause try catch, ça empêche justement la propagation de l'exception et de ce fait l'arrêt du script."
Non. Ca n'évite pas l'arrêt du script (parce qu'il est impossible de sortir du bloc catch pour reprendre où on en était dans le try), mais ça permet de gérer la manière dont il s'arrête.
Ca permet d'avoir un traitement de l'erreur aussi complet qu'on le souhaite.
On peut avoir une classe dbException qui permet de loguer la dernière requête SQL plantée qui a causé l'exception, ou bien de loguer le temps d'exécution s'il est trop long, ou que sais-je.
Parce que la gestion d'une erreur ne se limite pas à l'affichage d'un message, mais peut impliquer l'envoi d'un mail à un admin, un log d'opérations, une mise à jour d'un fichier, etc.
Messages postés
2483
Date d'inscription
jeudi 30 novembre 2006
Statut
Membre
Dernière intervention
14 janvier 2011
18
@coucou747 : oui et non.
J'en parlais avec Malalam à je ne sais plus quelle occasion, on parlait de standard et d'ouverture, avec des classes spécialisées ou non suivant ce qu'on fait.
Par exemple, un gestionnaire i18n (ah voilà, c'était à cette occasion là).
On peut soit imposer d'utiliser des fichiers XML pour organiser les traductions, dans le cas d'une application spécifique. On peut aussi vouloir, dans le cadre d'un framework le plus ouvert et portable possible, pouvoir utiliser des fichiers XML, des bases de données, des fichiers txt ou php, ou un dictionnaire électronique.
Ca reste à l'appréciation du développeur et ça dépend du contexte.
Soit on développe une classe séparément et on peut lever une exception, parce que c'est logique.
Soit on développe une application dont la classe n'est qu'une brique parmi tant d'autres, et elle travaille en collaboration avec ses copines.
Le mieux, c'est encore, pour être cohérent avec les deux et le plus ouvert possible, de permettre dans la configuration de la classe, de lever ou non une exception suivant les différentes erreurs qui peuvent survenir. Comme ça, la même classe est utilisable avec exceptions, et sans exceptions si on veut quand même continuer son script sans base de données.