Digamos que tengo Door
s que son administrados por a DoorService
. El DoorService
se encarga de abrir, cerrar y bloquear las puertas que están almacenadas en la base de datos.
public interface DoorService
{
void open(Door door) throws DoorLockedException, DoorAlreadyOpenedException;
void close(Door door) throws DoorAlreadyClosedException;
/**
* Closes the door if open
*/
void lock(Door door) throws DoorAlreadyLockedException;
}
Para el método de bloqueo, hay un DoorAlreadyLockedException
y para el método abierto hay un DoorLockedException
. Esta es una opción, pero hay otras opciones posibles:
1) Usar DoorLockedException
para todo, es un poco incómodo al detectar la excepción en una lock()
llamada
try
{
doorService.lock(myDoor);
}
catch(DoorLockedException ex) // door ALREADY locked
{
//error handling...
}
2) Tener los 2 tipos de excepciones DoorLockedException
yDoorAlreadyLockedException
3) Tener los 2 tipos de excepciones pero dejar DoorAlreadyLockedException extends DoorLockedException
¿Cuál es la mejor solución y por qué?
java
exceptions
class-design
Invierno
fuente
fuente
Respuestas:
Sé que las personas hacen un gran problema al usar excepciones para el control de flujo, pero ese no es el mayor problema aquí. Veo una violación de separación de consulta de comando enorme . Pero incluso eso no es lo peor.
No, lo peor está aquí:
¿Por qué demonios explota todo lo demás si su suposición sobre el estado de las puertas es incorrecta, pero
lock()
solo la arregla por usted? Olvídese de que esto hace que sea imposible cerrar la puerta, lo cual es absolutamente posible y ocasionalmente útil. No, el problema aquí es que has mezclado dos filosofías diferentes para tratar con suposiciones incorrectas. Eso es confuso No hagas eso. No al mismo nivel de abstracción con el mismo estilo de denominación. ¡Ay! Tome una de esas ideas afuera. Los métodos de servicio de la puerta deberían funcionar de la misma manera.En cuanto a la violación de Separación de consulta de comando, no debería tener que intentar cerrar una puerta para averiguar si está cerrada o abierta. Debería poder preguntar. El servicio de puerta no proporciona una manera de hacerlo sin posiblemente cambiar el estado de la puerta. Eso hace que esto sea mucho peor que los comandos que también devuelven valores (un malentendido común de lo que se trata CQS). ¡Aquí, los comandos de cambio de estado son la única forma de hacer consultas! ¡Ay!
En cuanto a que las excepciones son más caras que los códigos de estado, eso es hablar de optimización. Lo suficientemente rápido es lo suficientemente rápido. No, el verdadero problema es que los humanos no esperan excepciones para casos típicos. Puedes discutir sobre lo que es típico todo lo que te gusta. Para mí, la gran pregunta es qué tan legible está haciendo el código de uso.
Por favor, no me hagas escribir código como este. Por favor dénos
isLocked()
yisClosed()
métodos. Con ellos puedo escribir los míosensureClosed()
y losensureUnlocked()
métodos que son fáciles de leer. Los que solo tiran si se violan sus condiciones de publicación. Prefiero descubrir que ya los ha escrito y probado, por supuesto. Simplemente no los mezcle con los que arrojan cuando no pueden cambiar el estado. Como mínimo, deles nombres distintivos.Hagas lo que hagas, no llames a nada
tryClose()
. Ese es un nombre terrible.En cuanto a
DoorLockedException
solo vs también tenerDoorAlreadyLockedException
mal decir esto: se trata de usar el código. No diseñe servicios como este sin escribir el código de uso y mirar el desorden que está creando. Refactorice y rediseñe hasta que el código de uso sea al menos legible. De hecho, considere escribir primero el código de uso.fuente
lock()
método queclose()
me llamaba era que yo fuera flojo cuando escribía la pregunta, ¡pero gracias por hacerme consciente de eso! No caeré en ello cuando escriba código real. En mi caso, los 3 métodos nunca se llaman sucesivamente. Es solo: llame a uno, si no hay una buena excepción, de lo contrario, muestre un diálogo traducido y posiblemente cierre la sesión (si InvalidTokenException). TengoisLocked()
yisOpen()
métodos, y se usan en el flujo de control del servidor, pero no se deben usar como una forma de verificar si se debe mostrar un cuadro de diálogo de error, ese es un trabajo para una excepción.DoorAlreadyLockedException
pero hace clases gemelas.public class DoorAlreadyLockedException inherits DoorLockedException {}
. Defina una clase en una línea solo por darle un buen nombre. Elige lo que heredas sabiamente. Prefiero heredar lo más alto que puedo, sin ser innecesariamente redundante, para evitar una larga cadena de herencia.NoOneGaveMeAKey
aNoOneGaveMeAKeyException
solo para ser pendantic. De lo contrario, estoy de acuerdo con el punto principal, que es que antes del procesamiento, debemos poder conocer el estado del objeto.isOpen
/isClosed
no es lo suficientemente bueno. Piensa en el sistema de archivos. Puede verificar que el archivo exista / no exista, pero eso aún puede cambiar para cuando intente leerlo o escribirlo, y seIOException
obtendrá un resultado. Tal vez en este contexto realmente existe la posibilidad de que el estado no sea válido al abrir o cerrar la puerta.Hacer una distinción
DoorLockedException
yDoorAlreadyLockedException
sugiere que se están utilizando diferentes tipos de excepción para el control de flujo, una práctica que ya se considera ampliamente un antipatrón.Tener múltiples tipos de excepción para algo tan benigno es gratuito. La complejidad adicional no se ve compensada por los beneficios adicionales.
Solo úsalo
DoorLockedException
para todo.Mejor aún, simplemente no hagas nada. Si la puerta ya está cerrada, seguirá estando en el estado correcto cuando
lock()
se complete el método. No arroje excepciones para condiciones que no sean notables.Si todavía te sientes incómodo con eso, cambia el nombre de tu método
ensureDoorLocked()
.fuente
DoorLockedException
yDoorAlreadyLockedException
sugiera que se trata de tener un controlador de errores mucho más significativo para ellock()
método a costa de tener una clase casi duplicada.El tema de esta pregunta "¿Deberían favorecerse los tipos de excepciones reciclados sobre los de un solo uso?" parece haber sido ignorado, no solo en las respuestas, sino también en los detalles de las preguntas (las respuestas fueron a los detalles de la pregunta).
Estoy de acuerdo con la mayoría de las críticas al código que los encuestados han ofrecido.
Pero como respuesta a la pregunta principal, diría que debería preferir la reutilización de las 'excepciones recicladas' sobre las 'de un solo uso'.
En el uso más típico del manejo de excepciones, tiene una gran DISTANCIA en el código entre el lugar donde se detecta y se lanza la excepción, y el lugar donde se captura y maneja la excepción. Eso significa que el uso de tipos privados (modulares) en el manejo de excepciones generalmente no funcionará bien. Un programa típico con manejo de excepciones: tendrá un puñado de ubicaciones de nivel superior en el código donde se manejan las excepciones. Y como se trata de ubicaciones de nivel superior, es difícil para ellos tener un conocimiento detallado sobre aspectos estrechos de módulos particulares.
En cambio, es probable que tengan controladores de alto nivel para algunos problemas de alto nivel.
La única razón por la que el uso de clases de excepción personalizadas detalladas parece tener sentido en su muestra es porque (y aquí estoy parroteando a los otros encuestados), no debería usar el manejo de excepciones para el flujo de control ordinario.
fuente
Una alternativa inexplorada. Es posible que esté modelando lo incorrecto con sus clases, ¿y si tuviera estos en su lugar?
Ahora es imposible intentar algo que sea un error. No necesitas ninguna excepción.
fuente
Contestaré la pregunta original, en lugar de criticar el ejemplo.
En pocas palabras, si espera que el cliente deba reaccionar de manera diferente para cada caso, garantiza un nuevo tipo de excepción.
Si decide usar varios tipos de excepción y, sin embargo, espera que el cliente en algunos casos quiera crear una cláusula catch común para ellos, probablemente debería crear una superclase abstracta que ambos extiendan.
fuente
Este es un seudocódigo, pero creo que entenderás lo que quiero decir:
Sí, la reutilización del tipo de excepción tiene sentido, si usa excepciones para cosas verdaderamente excepcionales . Porque las cosas verdaderamente excepcionales son en su mayoría preocupaciones transversales. Estaba utilizando Excepciones básicamente para flujos de control y eso conduce a esos problemas.
fuente
DoorStuckException
yHouseOnFireException
no deben verificarse, no deben capturarse en todas las llamadas.