web-dev-qa-db-fra.com

Try / Catch dans Constructor - Pratique recommandée?

Quelque chose dont j'ai toujours été curieux

public class FileDataValidator {

private String[] lineData;

public FileDataValidator(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
    }
    catch(InvalidFormatException e)
    {
        e.printStackTrace();
    }

}

//validation methods below all throwing InvalidFormatException

N'est-il pas conseillé d'inclure le bloc try/catch dans mon constructeur? Je sais que je pourrais demander au constructeur de renvoyer l'exception à l'appelant. Que préférez-vous pour appeler des méthodes comme je l'ai fait dans Constructor? Dans la classe appelante, préférez-vous créer une instance de FileDataValidator et y appeler les méthodes sur cette instance? Je suis juste intéressé à entendre quelques commentaires!

24
deanmau5

Dans le code que vous montrez, les problèmes de validation ne communiquent pas avec le code qui crée cette instance d'objet. Ce n'est probablement pas une BONNE CHOSE.

Variation 1:

Si vous interceptez l'exception dans la méthode/le constructeur, assurez-vous de renvoyer quelque chose à l'appelant. Vous pouvez mettre un champ isValid qui est défini sur true si tout fonctionne. Cela ressemblerait à ceci:

private boolean isValid = false;

public FileDataValidator(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
        isValid = true;
    }
    catch(InvalidFormatException e)
    {
        isValid = false;
    }
}

public boolean isValid() {
    return isValid;
}

Variation 2:

Ou vous pouvez laisser l'exception ou une autre exception se propager à l'appelant. Je l'ai montré comme une exception non vérifiée, mais faites ce qui fonctionne selon votre exception en traitant la religion:

public FileDataValidator(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
    }
    catch(InvalidFormatException e)
    {
        throw new com.myco.myapp.errors.InvalidDataException(e.getMessage());
    }

}

Variation 3:

La troisième méthode que je veux mentionner a un code comme celui-ci. Dans le code appelant, vous devez appeler le constructeur, puis appeler la fonction build() qui fonctionnera ou non.

String[] lineData = readLineData();
FileDataValidator onePerson = new FileDataValidator();
try {
    onePerson.build(lineData);
} catch (InvalidDataException e) {
    // What to do it its bad?
}

Voici le code de la classe:

public FileDataValidator() {
    // maybe you need some code in here, maybe not
}

public void build(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
    }
    catch(InvalidFormatException e)
    {
        throw new com.myco.myapp.errors.InvalidDataException(e.getMessage());
    }

}

Bien sûr, la fonction build() pourrait utiliser une méthode isValid() que vous appelez pour voir si elle est juste mais une exception me semble la bonne façon pour la fonction de construction.

Variation 4:

La quatrième méthode que je veux mentionner est ce que j'aime le plus. Il a un code comme celui-ci. Dans le code appelant, vous devez appeler le constructeur, puis appeler la fonction build() qui fonctionnera ou non.

Ce type suit le fonctionnement de JaxB et JaxRS, qui est une situation similaire à celle que vous avez.

  1. Une source externe de données - vous avez un fichier, ils ont un message entrant au format XML ou JSON.
  2. Code pour construire les objets - vous avez votre code, ils ont leurs bibliothèques de code fonctionnant selon les spécifications des différents JSR.
  3. La validation n'est pas liée à la construction des objets.

Le code appelant:

String[] lineData = readLineData();
Person onePerson = new Person();
FileDataUtilities util = new FileDataUtilities();
try {
    util.build(onePerson, lineData);
    util.validate(onePerson);
} catch (InvalidDataException e) {
    // What to do it its bad?
}

Voici le code de classe où résident les données:

public class Person {
    private Name name;
    private Age age;
    private Town town;
... lots more stuff here ...
}

Et le code utilitaire pour construire et valider:

public FileDataValidator() {
    // maybe you need some code in here, maybe not
}

public void build(Person person, String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();
    setNameFromData(person);
    setAgeFromData(person);
    setTownFromData(person);
}

public boolean validate(Person person) {

    try
    {
        validateName(person);
        validateAge(person);
        validateTown(person);
        return true;
    }
    catch(InvalidFormatException e)
    {
        throw new com.myco.myapp.errors.InvalidDataException(e.getMessage());
    }

}
22
Lee Meador

Vous devez considérer le modèle d'usine statique. Rendez votre constructeur de tous les arguments privé. Fournissez une méthode FileDataValidator (args ...) statique. Cela accepte et valide tous les arguments. Si tout va bien, il peut appeler le constructeur privé et renvoyer l'objet nouvellement créé. Si quelque chose échoue, lancez une exception pour informer l'appelant qu'il a fourni de mauvaises valeurs.

Je dois également mentionner que ceci: catch (Exception e) {printSomeThing (e); }

C'est l'anti-modèle le plus mortel que vous puissiez faire avec les exceptions. Oui, vous pouvez lire certaines valeurs d'erreur sur la ligne de commande, puis? L'appelant (qui a fourni les mauvaises valeurs) n'est pas informé des mauvaises valeurs, l'exécution du programme se poursuit.

3
gyorgyabraham

Je préfère que les exceptions soient traitées par le bout de code qui sait comment les traiter. Dans ce cas, je suppose que le morceau de code créant un FileDataValidator sait ce qui devrait se produire si les données du fichier ne sont pas valides et que les exceptions doivent être traitées là-bas (je préconise la propagation à l'appelant).

Tout en discutant des meilleures pratiques - le nom de classe FileDataValidator me sent. Si l'objet que vous créez stocke des données de fichier, je l'appellerais FileData - peut-être avec une méthode de validation? Si vous souhaitez uniquement valider vos données de fichier, une méthode statique suffira.

2
selig