Me han dicho que las Excepciones solo deben usarse en casos excepcionales. ¿Cómo sé si mi caso es excepcional?


99

Mi caso específico aquí es que el usuario puede pasar una cadena a la aplicación, la aplicación la analiza y la asigna a objetos estructurados. A veces, el usuario puede escribir algo no válido. Por ejemplo, su aporte puede describir a una persona pero pueden decir que su edad es "manzana". El comportamiento correcto en ese caso es revertir la transacción y decirle al usuario que ocurrió un error y que tendrá que volver a intentarlo. Puede haber un requisito para informar sobre cada error que podamos encontrar en la entrada, no solo el primero.

En este caso, sostuve que deberíamos lanzar una excepción. No estuvo de acuerdo y dijo: "Las excepciones deben ser excepcionales: se espera que el usuario pueda ingresar datos no válidos, por lo que este no es un caso excepcional". Realmente no sabía cómo argumentar ese punto, porque por definición de la palabra, él Parece estar en lo cierto.

Pero, entiendo que esta es la razón por la cual se inventaron Excepciones en primer lugar. Solía ​​ser que tenía que inspeccionar el resultado para ver si ocurría un error. Si no lo comprobaste, podrían pasar cosas malas sin que te des cuenta.

Sin excepciones, cada nivel de la pila necesita verificar el resultado de los métodos que llama y si un programador olvida ingresar en uno de estos niveles, el código podría proceder accidentalmente y guardar datos no válidos (por ejemplo). Parece más propenso a errores de esa manera.

De todos modos, siéntase libre de corregir cualquier cosa que haya dicho aquí. Mi pregunta principal es si alguien dice que las Excepciones deberían ser excepcionales, ¿cómo sé si mi caso es excepcional?


3
posible duplicado? Cuando lanzar una excepción . Aunque estaba cerrado allí, pero creo que encaja aquí. Todavía es un poco de filosofía, algunas personas y comunidades tienden a ver las excepciones como una especie de control de flujo.
thorsten müller

8
Cuando los usuarios son tontos, proporcionan una entrada no válida. Cuando los usuarios son inteligentes, juegan al proporcionar una entrada no válida. Por lo tanto, la entrada de usuario no válida no es una excepción.
Mouviciel

77
Además, no confunda una excepción , que es un mecanismo muy específico en Java y .NET, con un error que es un término mucho más genérico. Hay más en el manejo de errores que lanzar excepciones. Esta discusión toca los matices entre las excepciones y los errores .
Eric King

44
"Exceptional"! = "Raramente sucede"
ConditionRacer

3
Considero que las excepciones molestas de Eric Lippert son un consejo decente.
Brian

Respuestas:


87

Se inventaron excepciones para ayudar a facilitar el manejo de errores con menos desorden de código. Debe usarlos en los casos en que faciliten el manejo de errores con menos desorden de código. Este negocio de "excepciones solo para circunstancias excepcionales" se origina en un momento en que el manejo de excepciones se consideró un golpe de rendimiento inaceptable. Ese ya no es el caso en la gran mayoría de los códigos, pero la gente todavía dice la regla sin recordar la razón detrás de esto.

Especialmente en Java, que es quizás el lenguaje más amante de las excepciones jamás concebido, no debería sentirse mal por usar excepciones cuando simplifica su código. De hecho, la propia Integerclase de Java no tiene un medio para verificar si una cadena es un entero válido sin potencialmente arrojar un NumberFormatException.

Además, aunque no puede confiar solo en la validación de la interfaz de usuario, tenga en cuenta si su interfaz de usuario está diseñada correctamente, como usar un control giratorio para ingresar valores numéricos cortos, entonces un valor no numérico que lo convierta en el back-end realmente sería un Condición excepcional.


10
Buen golpe, allí. En realidad, en la aplicación en el mundo real que he diseñado, el impacto en el rendimiento hizo hacer una diferencia, y tuve que cambiarlo a no lanzar excepciones para ciertas operaciones de análisis.
Robert Harvey

17
No digo que todavía no haya casos en los que el impacto en el rendimiento sea una razón válida, pero esos casos son la excepción (juego de palabras) en lugar de la regla.
Karl Bielefeldt

11
@RobertHarvey El truco en Java es lanzar objetos de excepción prefabricados, en lugar de hacerlo throw new .... O, arroje excepciones personalizadas, donde se sobrescribe fillInStackTrace (). Entonces no debería notar ninguna degradación del rendimiento, por no hablar de golpes .
Ingo

3
+1: Exactamente lo que iba a responder. Úselo cuando simplifica el código. Las excepciones pueden proporcionar un código mucho más claro en el que no tiene que molestarse en verificar los valores de retorno en cada nivel de la pila de llamadas. (Sin embargo, como todo lo demás, si se usa de la manera incorrecta, puede hacer que su código sea un desastre horrible.)
Leo

44
@Brendan Supongamos que se produce alguna excepción, y el código de manejo de errores está 4 niveles por debajo en la pila de llamadas. Si usa un código de error, las 4 funciones que se encuentran sobre el controlador deben tener el tipo del código de error como su valor de retorno, y debe hacer una cadena if (foo() == ERROR) { return ERROR; } else { // continue }en cada nivel. Si lanza una excepción no marcada, no hay "ruidoso y redundante" si el error devuelve error ". Además, si pasa funciones como argumentos, el uso de un código de error puede cambiar la firma de la función a un tipo incompatible, aunque el error no ocurra.
Doval

72

¿Cuándo se debe lanzar una excepción? Cuando se trata de código, creo que la siguiente explicación es muy útil:

Una excepción es cuando un miembro no puede completar la tarea que se supone que debe realizar como lo indica su nombre . (Jeffry Richter, CLR a través de C #)

¿Por qué es útil? Sugiere que depende del contexto cuando algo debe manejarse como una excepción o no. En el nivel de las llamadas a métodos, el contexto viene dado por (a) el nombre, (b) la firma del método y (b) el código del cliente, que usa o se espera que use el método.

Para responder a su pregunta, debe echar un vistazo al código, donde se procesa la entrada del usuario. Podría verse más o menos así:

public void Save(PersonData personData) {  }

¿El nombre del método sugiere que se realiza alguna validación? No. En este caso, un PersonData no válido debería generar una excepción.

Suponga que la clase tiene otro método que se ve así:

public ValidationResult Validate(PersonData personData) {  }

¿El nombre del método sugiere que se realiza alguna validación? Si. En este caso, un PersonData no válido no debería generar una excepción.

Para poner las cosas juntas, ambos métodos sugieren que el código del cliente debería verse así:

ValidationResult validationResult = personRegister.Validate(personData);
if (validationResult.IsValid())
{
    personRegister.Save(personData)
}
else
{
    // Throw an exception? To answer this look at the context!
    // That is: (a) Method name, (b) signature and
    // (c) where this method is (expected) to be used.
}

Cuando no está claro si un método debe arrojar una excepción, entonces quizás se deba a un nombre o firma de método mal elegidos. Quizás el diseño de la clase no está claro. A veces es necesario modificar el diseño del código para obtener una respuesta clara a la pregunta si se debe lanzar una excepción o no.


Ayer hice un structllamado "ValidationResult" y estructuré mi código de la manera que usted describe.
Paul

44
No ayuda responder a su pregunta, pero me gustaría señalar que usted siguió implícita o deliberadamente el principio de separación de consulta de comando ( en.wikipedia.org/wiki/Command-query_separation ). ;-)
Theo Lenndorff

¡Buena idea! Un inconveniente: en su ejemplo, la validación se realiza dos veces: una vez durante Validate(devolver False si no es válido) y una vez durante Save(lanzar una excepción específica y bien documentada si no es válida). Por supuesto, el resultado de la validación podría almacenarse en caché dentro del objeto, pero eso agregaría complejidad adicional, ya que el resultado de la validación necesitaría ser invalidado en los cambios.
Heinzi

@Heinzi, estoy de acuerdo. Se podría refactorizar de modo que Validate()se llame dentro del Save()método, y se ValidationResultpodrían usar detalles específicos del mismo para construir un mensaje apropiado para la excepción.
Phil

3
Esto es mejor que la respuesta aceptada, creo. Lanzar cuando la llamada no puede hacer lo que se suponía que debía hacer.
Andy

31

Las excepciones deben ser excepcionales: se espera que el usuario pueda ingresar datos no válidos, por lo que este no es un caso excepcional

Sobre ese argumento:

  • Se espera que un archivo no exista, por lo que no es un caso excepcional.
  • Se espera que se pierda la conexión con el servidor, por lo que no es un caso excepcional.
  • Se espera que el archivo de configuración pueda ser confuso de modo que no sea un caso excepcional
  • Se espera que su solicitud a veces se caiga, por lo que ese no es un caso excepcional

Cualquier excepción que atrape, debe esperar porque, bueno, decidió atraparla. Y así, según esta lógica, nunca debes lanzar ninguna excepción que realmente planeas atrapar.

Por lo tanto, creo que "las excepciones deberían ser excepcionales" es una terrible regla general.

Lo que debe hacer depende del idioma. Diferentes idiomas tienen diferentes convenciones sobre cuándo se deben lanzar excepciones. Python, por ejemplo, arroja excepciones para todo y cuando estoy en Python, hago lo mismo. C ++, por otro lado, arroja relativamente pocas excepciones, y allí sigo su ejemplo. Puede tratar C ++ o Java como Python y lanzar excepciones para todo, pero está trabajando en desacuerdo con la forma en que el lenguaje espera que se use.

Prefiero el enfoque de Python, pero creo que es una mala idea usar otros idiomas.


1
@gnat, lo sé. Mi punto es que debes seguir las convenciones del lenguaje (en este caso Java) incluso si no son tus favoritas.
Winston Ewert

66
+1 "exceptions should be exceptional" is a terrible rule of thumb.Bien dicho! Esa es una de esas cosas que la gente simplemente repite sin pensar en ellas.
Andres F.

2
"Esperado" se define no por un argumento subjetivo o una convención, sino por el contrato de la API / función (esto puede ser explícito, pero a menudo solo está implícito). Las diferentes funciones / API / subsistemas pueden tener diferentes expectativas, por ejemplo, para algunas funcionalidades de nivel superior, un archivo no existente es un caso esperado para manejar (podría informar esto a un usuario a través de una GUI), para otras funciones de nivel inferior es probablemente no (y por lo tanto debería arrojar una excepción). Esta respuesta parece perder ese punto importante ...
mikera

1
@mikera, sí, una función debería (solo) lanzar las excepciones definidas en su contrato. Pero esa no es la pregunta. La pregunta es cómo decide cuál debería ser ese contrato. Sostengo que la regla general, "las excepciones deben ser excepcionales" no es útil para tomar esa decisión.
Winston Ewert

1
@ Supercat, no creo que realmente importe lo que termina siendo más común. Creo que la pregunta crítica es tener un defecto sano. Si no manejo explícitamente la condición de error, ¿mi código finge que no sucedió nada u obtengo un mensaje de error útil?
Winston Ewert

30

Siempre pienso en cosas como acceder al servidor de base de datos o una API web cuando pienso en excepciones. Espera que el servidor / API web funcione, pero en un caso excepcional podría no funcionar (el servidor está inactivo). Por lo general, una solicitud web puede ser rápida, pero en circunstancias excepcionales (alta carga) puede agotar el tiempo de espera. Esto es algo fuera de tu control.

Los datos de entrada de sus usuarios están bajo su control, ya que puede verificar lo que envían y hacer con ellos lo que quiera. En su caso, validaría la entrada del usuario incluso antes de intentar guardarla. Y tiendo a estar de acuerdo en que deben esperarse los usuarios que proporcionan datos no válidos, y su aplicación debe dar cuenta de ellos validando la entrada y proporcionando un mensaje de error fácil de usar.

Dicho esto, utilizo excepciones en la mayoría de mis configuradores de modelos de dominio, donde no debería haber absolutamente ninguna posibilidad de que entren datos no válidos. Sin embargo, esta es una última línea de defensa, y tiendo a construir mis formularios de entrada con reglas de validación enriquecidas , por lo que prácticamente no hay posibilidad de activar esa excepción del modelo de dominio. Entonces, cuando un setter espera una cosa y obtiene otra, es una situación excepcional, que no debería haber sucedido en circunstancias normales.

EDITAR (algo más a tener en cuenta):

Al enviar datos proporcionados por el usuario a la base de datos, usted sabe de antemano lo que debe y no debe ingresar en sus tablas. Esto significa que los datos se pueden validar contra algún formato esperado. Esto es algo que puedes controlar. Lo que no puede controlar es que su servidor falle en el medio de su consulta. Entonces, sabe que la consulta está bien y los datos se filtran / validan, prueba la consulta y aún falla, esta es una situación excepcional.

De manera similar con las solicitudes web, no puede saber si la solicitud expirará o no podrá conectarse antes de intentar enviarla. Por lo tanto, esto también garantiza un enfoque de prueba / captura, ya que no puede preguntarle al servidor si funcionará unos milisegundos más tarde cuando envíe la solicitud.


8
¿Pero por qué? ¿Por qué las excepciones son menos útiles para manejar problemas que son más esperados?
Winston Ewert

66
@Pinetree, verificar la existencia del archivo antes de abrir un archivo es una mala idea. El archivo podría dejar de existir entre la verificación y la apertura, el archivo podría no tener permiso que le permita abrirlo, y verificar la existencia y luego abrir el archivo requerirá dos costosas llamadas al sistema. Es mejor que intentes abrir el archivo y luego lidiar con la falla al hacerlo.
Winston Ewert

10
Hasta donde puedo ver, casi todas las fallas posibles se manejan mejor como recuperarse de la falla en lugar de tratar de verificar el éxito de antemano. Si usa o no excepciones o algo más indica que la falla es un tema separado. Prefiero excepciones porque no puedo ignorarlas accidentalmente.
Winston Ewert

11
No estoy de acuerdo con su premisa de que debido a que se esperan datos de usuario no válidos, no pueden considerarse excepcionales. Si escribo un analizador sintáctico y alguien le proporciona datos que no se pueden analizar, esa es una excepción. No puedo seguir analizando. Cómo se maneja la excepción es otra cuestión completamente diferente.
ConditionRacer

44
File.ReadAllBytes arrojará un FileNotFoundExceptioncuando se le dé una entrada incorrecta (por ejemplo, un nombre de archivo inexistente). Esa es la única forma válida de amenazar este error, ¿qué más puede hacer sin dar como resultado la devolución de códigos de error?
oɔɯǝɹ

16

Referencia

Del programador pragmático:

Creemos que las excepciones rara vez deben usarse como parte del flujo normal de un programa; las excepciones deben reservarse para eventos inesperados. Suponga que una excepción no detectada terminará su programa y pregúntese: "¿Se seguirá ejecutando este código si elimino todos los controladores de excepciones?" Si la respuesta es "no", entonces quizás se estén utilizando excepciones en circunstancias no excepcionales.

Continúan examinando el ejemplo de abrir un archivo para leer, y el archivo no existe. ¿Debería plantear una excepción?

Si el archivo debería haber estado allí, se garantiza una excepción. [...] Por otro lado, si no tiene idea de si el archivo debe existir o no, entonces no parece excepcional si no puede encontrarlo, y es apropiado un retorno de error.

Más tarde, discuten por qué eligieron este enfoque:

[Una] n excepción representa una transferencia de control inmediata y no local: es una especie de cascada goto. Los programas que usan excepciones como parte de su procesamiento normal sufren todos los problemas de legibilidad y facilidad de mantenimiento del código clásico de espagueti. Estos programas rompen la encapsulación: las rutinas y sus llamadores están más estrechamente acoplados a través del manejo de excepciones.

Con respecto a su situación

Su pregunta se reduce a "¿Deben los errores de validación generar excepciones?" La respuesta es que depende de dónde esté ocurriendo la validación.

Si el método en cuestión está dentro de una sección del código donde se supone que los datos de entrada ya han sido validados, los datos de entrada no válidos deberían generar una excepción; Si el código está diseñado de tal manera que este método recibirá la entrada exacta ingresada por un usuario, se esperan datos no válidos y no se debe generar una excepción.


11

Aquí hay una gran cantidad de pontificación filosófica, pero en términos generales, las condiciones excepcionales son simplemente aquellas condiciones que no puede o no quiere manejar (aparte de la limpieza, informes de errores y similares) sin la intervención del usuario. En otras palabras, son condiciones irrecuperables.

Si le entrega a un programa una ruta de archivo, con la intención de procesar ese archivo de alguna manera, y el archivo especificado por esa ruta no existe, es una condición excepcional. No puede hacer nada al respecto en su código, aparte de informarlo al usuario y permitirle especificar una ruta de archivo diferente.


1
+1, muy cerca de lo que iba a decir. Diría que se trata más del alcance, y realmente no tiene nada que ver con el usuario. Un buen ejemplo de esto es la diferencia entre las dos funciones .Net int.Parse e int. TryParse, la primera no tiene más remedio que lanzar una excepción en una entrada incorrecta, la última nunca debe arrojar una excepción
jmoreno

1
@ jmoreno: Ergo, usarías TryParse cuando el código podría hacer algo sobre la condición no analizable, y Parse cuando no podría.
Robert Harvey

7

Hay dos preocupaciones que debe considerar:

  1. discute una sola preocupación, llamémosla Assignerya que esta preocupación es asignar entradas a objetos estructurados y expresa la restricción de que sus entradas sean válidas

  2. Una interfaz de usuario bien implementada tiene una preocupación adicional: la validación de la entrada del usuario y la retroalimentación constructiva sobre los errores (llamemos a esta parte Validator)

Desde el punto de vista del Assignercomponente, lanzar una excepción es totalmente razonable, ya que ha expresado una restricción que ha sido violada.

Desde el punto de vista de la experiencia del usuario , el usuario no debería estar hablando directamente de esto Assigneren primer lugar. Deberían hablar a ella a través de la Validator.

Ahora, en la Validatorentrada de usuario inválida no es un caso excepcional, es realmente el caso en el que está más interesado. Por lo tanto, aquí una excepción no sería apropiada, y aquí también es donde querría identificar todos los errores en lugar de rescatando a la primera.

Notarás que no mencioné cómo se implementan estas preocupaciones . Parece que estás hablando de Assignery tu colega está hablando de una combinación Validator+Assigner. Una vez que te das cuenta que son dos preocupaciones separados (o separables), al menos se puede hablar con sensatez.


Para abordar el comentario de Renan, solo estoy asumiendo que una vez que haya identificado sus dos preocupaciones separadas, es obvio qué casos deben considerarse excepcionales en cada contexto.

De hecho, si no es obvio si algo debe considerarse excepcional, diría que probablemente no haya terminado de identificar las preocupaciones independientes en su solución.

Supongo que eso hace la respuesta directa a

... ¿cómo sé si mi caso es excepcional?

sigue simplificando hasta que sea obvio . Cuando tienes un montón de conceptos simples que entiendes bien, puedes razonar claramente sobre volver a componerlos en código, clases, bibliotecas o lo que sea.


-1 Sí, hay dos preocupaciones, pero esto no responde a la pregunta "¿Cómo sé si mi caso es excepcional?"
RMalke

El punto es que el mismo caso puede ser excepcional en un contexto y no en otro. Identificar de qué contexto estás hablando realmente (en lugar de combinarlos a ambos) responde la pregunta aquí.
Inútil

... en realidad, tal vez no. He abordado tu punto en mi respuesta.
Inútil

4

Otros han respondido bien, pero aún aquí está mi respuesta corta. La excepción es una situación en la que algo en el entorno está mal, lo que no puede controlar y su código no puede avanzar en absoluto. En este caso, también deberá informar al usuario qué salió mal, por qué no puede ir más allá y cuál es la resolución.


3

Nunca he sido un gran admirador de los consejos de que solo debes lanzar excepciones en casos que son excepcionales, en parte porque no dice nada (es como decir que solo debes comer alimentos comestibles), pero también porque es muy subjetivo y a menudo no está claro qué constituye un caso excepcional y qué no.

Sin embargo, hay buenas razones para este consejo: lanzar y capturar excepciones es lento, y si está ejecutando su código en el depurador en Visual Studio con él configurado para notificarle cada vez que se lanza una excepción, puede terminar recibiendo correo no deseado por docenas si no cientos de mensajes mucho antes de llegar al problema.

Entonces, como regla general, si:

  • su código está libre de errores y
  • los servicios de los que depende están todos disponibles, y
  • su usuario está utilizando su programa de la manera en que fue diseñado para ser utilizado (incluso si alguna de las entradas que proporciona no es válida)

entonces su código nunca debe arrojar una excepción, incluso una que se detecte más tarde. Para atrapar datos no válidos, puede usar validadores a nivel de UI o código como Int32.TryParse()en la capa de presentación.

Para cualquier otra cosa, debe atenerse al principio de que una excepción significa que su método no puede hacer lo que su nombre dice que hace. En general, no es una buena idea usar códigos de retorno para indicar un error (a menos que el nombre de su método indique claramente que lo hace, por ejemplo TryParse()) por dos razones. Primero, la respuesta predeterminada a un código de error es ignorar la condición de error y continuar independientemente; segundo, puede terminar fácilmente con algunos métodos usando códigos de retorno y otros métodos usando excepciones, y olvidando cuál es cuál. Incluso he visto bases de código donde dos implementaciones intercambiables diferentes de la misma interfaz adoptan enfoques diferentes aquí.


2

Las excepciones deben representar condiciones que es probable que el código de llamada inmediata no esté preparado para manejar, incluso si el método de llamada lo hace. Considere, por ejemplo, que el código que lee algunos datos de un archivo, puede suponer legítimamente que cualquier archivo válido terminará con un registro válido, y no es necesario extraer ninguna información de un registro parcial.

Si la rutina de lectura de datos no usó excepciones, sino que simplemente informó si la lectura tuvo éxito o no, el código de llamada tendría que verse así:

temp = dataSource.readInteger();
if (temp == null) return null;
field1 = (int)temp;
temp = dataSource.readInteger();
if (temp == null) return null;
field2 = (int)temp;
temp = dataSource.readString();
if (temp == null) return null;
field3 = temp;

etc. gastando tres líneas de código por cada trabajo útil. Por el contrario, si readIntegerarroja una excepción al encontrar el final de un archivo, y si la persona que llama simplemente puede pasar la excepción, entonces el código se convierte en:

field1 = dataSource.readInteger();
field2 = dataSource.readInteger();
field3 = dataSource.readString();

Un aspecto mucho más simple y limpio, con un énfasis mucho mayor en el caso en que las cosas funcionan normalmente. Tenga en cuenta que en los casos en que el llamante inmediato esperaría manejar una condición, un método que devuelve un código de error a menudo será más útil que uno que arroje una excepción. Por ejemplo, para totalizar todos los enteros en un archivo:

do
{
  temp = dataSource.tryReadInteger();
  if (temp == null) break;
  total += (int)temp;
} while(true);

versus

try
{
  do
  {
    total += (int)dataSource.readInteger();
  }
  while(true);
}
catch endOfDataSourceException ex
{ // Don't do anything, since this is an expected condition (eventually)
}

El código que solicita los enteros espera que una de esas llamadas falle. Hacer que el código use un bucle sin fin que se ejecutará hasta que eso ocurra es mucho menos elegante que usar un método que indique fallas a través de su valor de retorno.

Debido a que las clases a menudo no sabrán qué condiciones esperarán o no sus clientes, a menudo es útil ofrecer dos versiones de métodos que podrían fallar de una manera que algunas personas que llaman esperarán y otras no. Hacerlo permitirá que dichos métodos se usen limpiamente con ambos tipos de personas que llaman. Tenga en cuenta también que incluso los métodos "probar" deberían arrojar excepciones si surgen situaciones que la persona que llama probablemente no está esperando. Por ejemplo, tryReadIntegerno debería lanzar una excepción si encuentra una condición de final de archivo limpia (si la persona que llama no esperaba eso, la persona que llama habría utilizadoreadInteger) Por otro lado, probablemente debería arrojar una excepción si los datos no se pueden leer porque, por ejemplo, la tarjeta de memoria que los contenía estaba desconectada. Si bien dichos eventos siempre deben reconocerse como una posibilidad, es poco probable que el código de llamada inmediata esté preparado para hacer algo útil en respuesta; ciertamente no debe informarse de la misma manera que sería una condición de fin de archivo.


2

Lo más importante al escribir software es hacerlo legible. Todas las demás consideraciones son secundarias, incluida la eficiencia y la corrección. Si es legible, el resto se puede ocupar en el mantenimiento, y si no es legible, entonces es mejor tirarlo. Por lo tanto, debe lanzar excepciones cuando mejore la legibilidad.

Cuando esté escribiendo algún algoritmo, solo piense en la persona en el futuro que lo leerá. Cuando llegue a un lugar donde podría haber un problema potencial, pregúntese si el lector quiere ver cómo maneja ese problema ahora , o ¿preferiría simplemente seguir con el algoritmo?

Me gusta pensar en una receta para pastel de chocolate. Cuando le dice que agregue los huevos, tiene una opción: puede asumir que tiene huevos y continuar con la receta, o puede comenzar una explicación de cómo puede obtener huevos si no tiene huevos. Podría llenar un libro completo con técnicas para cazar pollos salvajes, todo para ayudarlo a hornear un pastel. Eso es bueno, pero la mayoría de las personas no querrán leer esa receta. La mayoría de las personas preferiría asumir que los huevos están disponibles y seguir con la receta. Esa es una decisión que los autores deben hacer al escribir recetas.

No puede haber reglas garantizadas sobre qué es una buena excepción y qué problemas deben manejarse de inmediato, porque requiere que leas la mente de tu lector. Lo mejor que harás es reglas generales, y "las excepciones son solo para circunstancias excepcionales" es una muy buena. Por lo general, cuando un lector lee su método, está buscando lo que el método hará el 99% del tiempo, y preferiría no tener eso lleno de casos extraños en las esquinas, como tratar con usuarios que ingresan entradas ilegales y otras cosas que casi nunca suceden. Quieren ver el flujo normal de su software directamente, una instrucción tras otra, como si nunca ocurrieran problemas.


2

Puede haber un requisito para informar sobre cada error que podamos encontrar en la entrada, no solo el primero.

Es por eso que no puedes lanzar una excepción aquí. Una excepción interrumpe inmediatamente el proceso de validación. Por lo tanto, habría mucho trabajo alternativo para hacer esto.

Un mal ejemplo:

Método de validación para la Dogclase usando excepciones:

void validate(Set<DogValidationException> previousExceptions) {
    if (!DOG_NAME_PATTERN.matcher(this.name).matches()) {
        DogValidationException disallowedName = new DogValidationException(Problem.DISALLOWED_DOG_NAME);
        if (!previousExceptions.contains(disallowedName)){
            throw disallowedName;
        }
    }
    if (this.legs < 4) {
        DogValidationException invalidDog = new DogValidationException(Problem.LITERALLY_INVALID_DOG);
        if (!previousExceptions.contains(invalidDog)){
            throw invalidDog;
        }
    }
    // etc.
}

Cómo llamarlo:

Set<DogValidationException> exceptions = new HashSet<DogValidationException>();
boolean retry;
do {
    retry = false;
    try {
        dog.validate(exceptions);
    } catch (DogValidationException e) {
        exceptions.add(e);
        retry = true;
    }
} while (retry);

if(exceptions.isEmpty()) {
    dogDAO.beginTransaction();
    dogDAO.save(dog);
    dogDAO.commitAndCloseTransaction();
} else {
    // notify user to fix the problems
}

El problema aquí es que el proceso de validación, para obtener todos los errores, requeriría omitir las excepciones ya encontradas. Lo anterior podría funcionar, pero este es un uso indebido claro de las excepciones . El tipo de validación que se le solicitó debe realizarse antes de tocar la base de datos. Por lo tanto, no es necesario revertir nada. Y es probable que los resultados de la validación sean errores de validación (aunque con suerte cero).

El mejor enfoque es:

Llamada al método:

Set<Problem> validationResults = dog.validate();
if(validationResults.isEmpty()) {
    dogDAO.beginTransaction();
    dogDAO.save(dog);
    dogDAO.commitAndCloseTransaction();
} else {
    // notify user to fix the problems
}

Método de validación:

Set<Problem> validate() {
    Set<Problem> result = new HashSet<Problem>();
    if(!DOG_NAME_PATTERN.matcher(this.name).matches()) {
        result.add(Problem.DISALLOWED_DOG_NAME);
    }
    if(this.legs < 4) {
        result.add(Problem.LITERALLY_INVALID_DOG);
    }
    // etc.
    return result;
}

¿Por qué? Hay toneladas de razones, y la mayoría de las razones se han señalado en las otras respuestas. En pocas palabras: es mucho más simple de leer y comprender por otros. En segundo lugar, ¿desea mostrar los rastros de la pila de usuarios para explicarle que configuró los suyos dogincorrectamente?

Si , durante la confirmación en el segundo ejemplo, aún surge un error , a pesar de que su validador lo haya validado dogcon cero problemas, entonces lanzar una excepción es lo correcto . Me gusta: Sin conexión a la base de datos, la entrada de la base de datos ha sido modificada por otra persona mientras tanto, o algo así.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.