2013-05-28 15 views
16

Qualcosa Sono sempre stato curioso diTry/Catch in Constructor - Esercizi raccomandati?

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 

È non è necessario prevedere il blocco try/catch all'interno del mio Constructor? So che potrei fare in modo che il Costruttore restituisca l'Eccezione al chiamante. Cosa preferite voi ragazzi nel chiamare metodi come ho fatto in Constructor? Nella classe chiamante preferiresti creare un'istanza di FileDataValidator e chiamare i metodi lì su quell'istanza? Sono solo interessato ad ascoltare un feedback!

+3

Più informazioni è cosa si farebbe con 'e' nel caso di un'API,' printStackTrace' ha un odore divertente. Sicuramente dovresti lasciare che gli utenti del codice incontrino l'eccezione in modo che possano fare qualcosa al riguardo? Ecco a cosa servono le eccezioni. –

+0

Perché non modificare le operazioni 'validateXXX' per restituire i booleani e quindi impostare una variabile chiamata' valid' da essere se tutte e tre le chiamate 'validateXXX' sono valide. Esporre quindi var con un metodo 'isValid' – cmbaxter

+0

Fare qualcosa con il [Command Pattern] (http://en.wikipedia.org/wiki/Command_pattern) può aiutare qui; cioè, creare un'istanza di un metodo che chiamerai più volte, passare i dati per convalidare e lasciare che il metodo faccia il lancio dell'eccezione. –

risposta

15

Nel codice visualizzato, i problemi di convalida non comunicano nuovamente al codice che sta creando questa istanza dell'oggetto. Probabilmente non è una COSA BUONA.

Variante 1:

Se si cattura l'eccezione all'interno del metodo/costruttore, essere sicuri di passare qualcosa al chiamante. È possibile inserire un campo isValid impostato su true se tutto funziona. Che sarebbe simile a questa:

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; 
} 

Variante 2:

o si può lasciare che l'eccezione o qualche altra eccezione si propagano al chiamante. Ho mostrato come eccezioni non controllato, ma fare quello che funziona secondo la vostra gestione religione eccezione:

public FileDataValidator(String[] lineData){ 

    this.lineData = lineData; 
    removeLeadingAndTrailingQuotes(); 

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

} 

Variante 3:

Il terzo metodo che voglio citare è il codice come questo. Nel codice chiamante devi chiamare il costruttore e quindi chiamare la funzione build() che funzionerà o meno.

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

Ecco il codice della 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()); 
    } 

} 

Naturalmente, la funzione build() potrebbe utilizzare un metodo isValid() che si chiama per vedere se il suo diritto, ma un'eccezione sembra il modo giusto per me per la costruire la funzione.

Variation 4:

Il quarto metodo che voglio citare è quello che mi piace di più. Ha un codice come questo. Nel codice chiamante devi chiamare il costruttore e quindi chiamare la funzione build() che funzionerà o meno.

Questo tipo segue il modo in cui funzionano JaxB e JaxRS, una situazione simile a quella che si ha.

  1. Una fonte esterna di dati: si dispone di un file, hanno un messaggio in arrivo in formato XML o JSON.
  2. Codice per creare gli oggetti: si dispone del codice, hanno le loro librerie di codice che funzionano secondo le specifiche delle varie JSR.
  3. La convalida non è legata alla costruzione degli oggetti.

Il codice chiamante:

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? 
} 

Ecco il codice della classe in cui i dati vive:

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

E il codice di utilità per costruire e validare:

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()); 
    } 

} 
+0

Il primo tipo è rotto (metodi _within_ il costruttore?) E il secondo è solo un esercizio di ridondanza. –

+0

@GrantThomas mi piacerebbe saperne di più. Non vedo problemi nel chiamare i metodi nel costruttore, se servono a uno scopo utile. Forse potresti spiegare. OP dovrebbe decidere cosa è utile. Secondo, stai dicendo che il "catch-rethrow" è ridondante? –

+1

@LeeMeador Grazie per averci dedicato un sacco di tempo a scrivere diverse idee! Ci hanno fornito un sacco di spunti di riflessione. Penso che andrò con la variazione 3. Spostare le chiamate di convalida dal costruttore e propagare l'eccezione al chiamante è molto più amichevole in termini di poter riutilizzare un singolo oggetto, e la classe di chiamata è molto più adatta a sapere cosa dovrebbe accadere se i dati non validi sono stati passati. – deanmau5

2

La mia preferenza è che le eccezioni vengano gestite dal bit di codice che sa come affrontarli In questo caso, suppongo che il bit di codice che crea un FileDataValidator sappia cosa dovrebbe accadere se i dati del file non sono validi e le eccezioni dovrebbero essere trattate lì (sto sostenendo la propagazione al chiamante).

Mentre si discute delle migliori pratiche, il nome della classe FileDataValidator mi annusa. Se l'oggetto che stai creando memorizza i dati del file, allora lo chiamerei FileData - forse con un metodo di validazione? Se si desidera solo convalidare i dati del file, sarebbe sufficiente un metodo statico.

+0

+1 per aver notato che il nome della classe deve essere migliorato. –

2

È necessario considerare il modello di fabbrica statico. Rendi privato il costruttore di tutti gli argomenti. Fornire un metodo FileDataValidator (args ...) statico. Questo accetta e convalida tutti gli argomenti. Se tutto va bene, può chiamare il costruttore privato e restituire l'oggetto appena creato. In caso di problemi, lanciare un'eccezione per informare il chiamante che ha fornito valori errati.

Devo anche menzionare che questo: cattura (Eccezione e) { printSomeThing (e); }

È l'antipattern più micidiale che si possa fare con Eccezioni. Sì, puoi leggere alcuni valori di errore sulla riga di comando e poi? Il chiamante (che ha fornito i valori errati) non viene informato dei valori errati, l'esecuzione del programma continuerà.

+0

Grazie per aver suggerito questo patter. Im molto per l'implementazione di modelli il mio codice e questo è qualcosa che certamente giocherò con. Grazie anche per i consigli anti-modello! Ad essere onesti, ce l'ho solo per scopi di sviluppo finché non risolvo la soluzione alla domanda iniziale! – deanmau5