2009-11-19 3 views
0

Je sais que ce type de question a été posé à maintes reprises, mais je n'ai pas encore trouvé de réponse définitive au problème que j'examine. De tout le contenu sur la gestion des exceptions que j'ai lu, il semble que le consensus général est que les exceptions ne doivent être utilisées que dans des circonstances exceptionnelles. J'ai également lu dans de nombreux endroits que l'on devrait utiliser, si possible, renvoyer des valeurs pour indiquer des problèmes ou des échecs (tels que l'échec de connexion, l'échec de validation de quelque sorte). Mon problème est, quand on utilise ces valeurs de retour, comment communique-t-on l'information contextuelle du problème? Avec des exceptions, on peut ajouter les informations contextuelles à l'exception et permettre que cela se produise. Laissez-moi essayer et utiliser un exemple de code pour expliquer:Exceptions, valeurs de retour et informations contextuelles

Disons que nous avons une classe abstraite de base (j'ai omis certains détails) qui représente une sorte de définition de format pour une chaîne. Cette classe dicte essentiellement comment le format d'une chaîne donnée devrait être.

public abstract class ADataEntryDefinition 
{ 
    public boolean isValid(String data); 
} 

disons que je l'étendre à effectuer une validation de la sécurité sur la chaîne:

public class SecureDataEntryDefinition extends ADataEntryDefinition 
{ 
    public boolean isValid(String data) 
    { 
     //do some security checks on the format of the data 
    }   
} 

La méthode validate prendra dans une chaîne et retourne true si la chaîne correspond à la définition de données définies par la classe . En avançant, disons que j'ai une classe qui gère plusieurs de ces définitions de données, et que cette classe a la responsabilité de valider chaque entrée dans une chaîne séparée par des virgules par rapport à l'une des définitions de données qu'elle maintient.

public class DataSetDefinitions 
{ 
    private List<ADataEntryDefinition> dataDefinitions = ... 

    public boolean isValid(String dataValues) 
    { 
     //obtain each string in dataValues delimited by a ',' into String[] 
     //called dataEntryValues 

     int i=0; 
     for (ADataEntryDefinition dataEntry : dataDefinitions) 
     { 
      if (!dataEntry.isValid(dataEntryValues[i++]) 
      { 
       return false; 
      } 
     } 
     return true;   
    }   
} 

Maintenant, pour moi, ces méthodes semblent façon générale à jeter des exceptions en cas de données non valides (pour un, des données non valides peuvent être attendus dans certains cas). Dans ce cas, j'aime l'approche consistant à renvoyer true/false pour indiquer l'échec de la validation et permettre ensuite à l'appelant de juger de la gravité de cette erreur. Ainsi, l'appelant effectue les opérations suivantes:

boolean success = false; 
success = dataSetDefinitions.isValid(someString); 

Supposons un appelant spécifique comme l'estimera ci-dessus la validation pas être critique, et, par conséquent, doit ensuite lancer une exception pour empêcher le traitement de continuer; où devrait-il obtenir l'information contextuelle dont il a besoin pour transmettre le problème ... comment devrait-il savoir que 2 couches (appels) vers le bas de la validation ont échoué en raison de problèmes de sécurité dans la classe SecureDataEntryDefinition (ou n'importe quelle autre sous-classe d'ailleurs) .

Je suppose que je pourrais ajouter une méthode comme ceci:

public class DataSetDefinitions 
{ 
    private List<ADataEntryDefinition> dataDefinitions = ... 

    public boolean isValid(String dataValues) 
    { 
     .... 
    } 

    public String getValidationErrorMsg() {...} 
} 

qui renvoie le message d'erreur de la dernière validation a échoué. Ensuite, ce qui suit pourrait se faire par l'appelant lors de la validation a échoué:

success = dataSetDefinitions.isValid(someString); 
if (!success) 
    throw new SomeException(dataSetDefinitions.getValidationErrorMsg()); 

Mais pour moi ce juste semble avoir la classe ( DataSetDefinitions dans ce cas) savent ou maintenir l'état de la validation précédente où il shouldn 't. Tenant compte du fait que cette classe peut effectuer la validation de plusieurs chaînes différentes et indépendantes, il semble erroné qu'elle conserve l'état de la validation de l'une d'entre elles.Je suppose que cette question est essentiellement de savoir comment on conçoit les méthodes pour être général - ne pas prendre la loi dans leurs propres mains en jetant des exceptions inutilement, mais permettant aux appelants de décider de la gravité - tout en permettant aux appelants d'obtenir des informations contextuelles détaillées. informations dans le cas où l'appelant a besoin de communiquer le problème. Y a-t-il une meilleure façon de faire ce qui précède? Excuses si cela a été très long:/Toutes les réponses seront appréciées.

Ciao.

Répondre

1

Ne pas retourner un bool. Renvoie une classe qui encapsule l'état succès/échec, plus les informations associées. De cette façon, vous pouvez faire quelque chose comme:

DataEntryStatus status = isValid(...); 

if (!status.isValid()) { 
    throw status.generateStatusException(); 
} 

et l'objet d'état lui-même génère l'exception appropriée, maintenant ainsi l'encapsulation.

1

Vous pouvez renvoyer une classe définie par l'utilisateur au lieu d'un simple bool afin de fournir plus d'informations contextuelles.

Ce serait quelque chose de similaire à la stratégie utilisée avec les événements. Nous avons une classe EventArgs à partir de laquelle d'autres classes dérivent afin de fournir plus d'informations contextuelles pour un type donné d'événement.

0

La façon dont je le résous la plupart du temps est de définir plusieurs constantes de classe et les retourner. Ensuite, dans la logique métier de mes contrôleurs, je voudrais simplement vérifier ces valeurs de manière statique.

<?php 
class Test 
{ 
    const SUCCESS = 1000; 
    const EMAIL_FAIL = 2001; 
    const SAVE_FAIL = 2002; 

    ... 

    public function save($value) 
    { 
     if (!$this->writetodb($value) 
     return self::SAVE_FAIL; 
     elseif(!$this->sendMailToAdmin()) 
     return self::EMAIL_FAIL; 
     else 
     return self::SUCCESS; 
    } 
} 




$test = new Test(); 
$result = $test->save('my value'); 
switch ($result) { 
    case Test::SUCCESS: 
     echo 'Yay!'; 
     break; 
    case Test::SAVE_FAIL: 
     echo 'Error saving!'; 
     break; 
    case Test::EMAIL_FAIL: 
     echo 'Error sending email!'; 
     break; 
} 
Questions connexes