java - Prueba/captura en el constructor-¿Práctica recomendada?
oop exception (3)
Algo de lo que siempre he tenido curiosidad.
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
¿No es recomendable incluir el bloque try / catch dentro de mi Constructor? Sé que podría hacer que el Constructor devuelva la Excepción a la persona que llama. ¿Qué prefieren ustedes en los métodos de llamada como lo he hecho en Constructor? ¿En la clase que llama preferiría crear una instancia de FileDataValidator y llamar a los métodos allí en esa instancia? Sólo estoy interesado en escuchar algunos comentarios!
Debes considerar el patrón de fábrica estático. Haz tu constructor de todos los argumentos privado. Proporcione un método estático FileDataValidator (args ...). Esto acepta y valida todos los argumentos. Si todo está bien, puede llamar al constructor privado y devolver el objeto recién creado. Si algo falla, lance una excepción para informar a la persona que llama que proporcionó valores incorrectos.
También debo mencionar que esto: catch (Exception e) {printSomeThing (e); }
Es el antipattern más letal que podrías hacer con Excepciones. Sí, puede leer algunos valores de error en la línea de comando, y luego? La persona que llama (quien proporcionó los valores incorrectos) no se informa de los valores incorrectos, la ejecución del programa continuará.
En el código que muestra, los problemas de validación no se comunican con el código que crea esta instancia de objeto. Probablemente no sea una BUENA COSA.
Variación 1:
Si detecta la excepción dentro del método / constructor, asegúrese de devolver algo a la persona que llama. Podría poner un campo isValid
que se establece en true si todo funciona. Eso se vería así:
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;
}
Variación 2:
O puede dejar que la excepción o alguna otra excepción se propague al llamante. Lo he mostrado como una excepción no comprobada, pero hago lo que sea que funcione de acuerdo con su religión de manejo de excepciones:
public FileDataValidator(String[] lineData){
this.lineData = lineData;
removeLeadingAndTrailingQuotes();
try
{
validateName();
validateAge();
validateTown();
}
catch(InvalidFormatException e)
{
throw new com.myco.myapp.errors.InvalidDataException(e.getMessage());
}
}
Variación 3:
El tercer método que quiero mencionar tiene un código como este. En el código de llamada, debe llamar al constructor y luego llamar a la función build()
que funcionará o no.
String[] lineData = readLineData();
FileDataValidator onePerson = new FileDataValidator();
try {
onePerson.build(lineData);
} catch (InvalidDataException e) {
// What to do it its bad?
}
Aquí está el código de clase:
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());
}
}
Por supuesto, la función build()
podría usar un método isValid()
que llamas para ver si está bien, pero una excepción me parece la forma correcta para la función build.
Variación 4:
El cuarto método que quiero mencionar es lo que más me gusta. Tiene un código como este. En el código de llamada, debe llamar al constructor y luego llamar a la función build()
que funcionará o no.
Este tipo de sigue la forma en que JaxB y JaxRS funcionan, que es una situación similar a la que tienes.
- Una fuente de datos externa: usted tiene un archivo, tienen un mensaje entrante en formato XML o JSON.
- Código para construir los objetos: usted tiene su código, ellos tienen sus bibliotecas de código que funcionan de acuerdo con las especificaciones de los distintos JSR.
- La validación no está ligada a la construcción de los objetos.
El código de llamada:
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?
}
Aquí está el código de clase donde viven los datos:
public class Person {
private Name name;
private Age age;
private Town town;
... lots more stuff here ...
}
Y el código de utilidad para construir y validar:
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());
}
}
Mi preferencia es que las excepciones sean tratadas por el bit de código que sabe cómo manejarlas. En este caso, supondría que la parte del código que crea un FileDataValidator sabe lo que debería suceder si los datos del archivo no son válidos, y las excepciones deberían tratarse allí (estoy recomendando la propagación a la persona que llama).
Mientras se discute sobre las mejores prácticas, el nombre de clase FileDataValidator me huele. Si el objeto que está creando almacena datos de archivos, lo llamaría FileData, ¿tal vez con un método de validación? Si solo desea validar los datos de su archivo, entonces un método estático sería suficiente.