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í:
/**
* Closes the door if open
*/
¿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.
ensureClosed(DoorService service, Door door){
// Need door closed and unlocked. No idea of its state. What to do?
try {
service.open(door)
service.close(door)
}
catch( DoorLockedException e ){
//Have no way to unlock the door so give up and die
log(e);
throw new NoOneGaveMeAKeyException(e);
}
catch( DoorAlreadyOpenedException e ){
try {
service.close(door);
}
catch( DoorAlreadyClosedException e ){
//Some multithreaded goof has been messing with our door.
//Oh well, this is what we wanted anyway.
//Hope they didn't lock it.
}
}
}
Por favor, no me hagas escribir código como este. Por favor dénos isLocked()
y isClosed()
métodos. Con ellos puedo escribir los míos ensureClosed()
y los ensureUnlocked()
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 tener DoorAlreadyLockedException
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.
ensureClosed(DoorService service, Door door){
if( !service.isClosed(door) ){
try{
service.close(door);
}
catch( DoorAlreadyClosedException e ){
//Some multithreaded goof has been messing with our door.
//Oh well, this is what we wanted anyway.
//Hope they didn't lock it.
}
} else {
//This is what you wanted, so quietly do nothing.
//Why are you even here? Who bothers to write empty else conditions?
}
}
ensureUnlocked(DoorService service, Door door){
if( service.islocked(door) ){
throw new NoOneGaveMeAKeyException();
}
}