Sei que as pessoas fazem muito uso de exceções para controle de fluxo, mas esse não é o maior problema aqui. Eu vejo uma enorme violação de separação de consulta de comando . Mas mesmo isso não é o pior.
Não, o pior está aqui:
/**
* Closes the door if open
*/
Por que diabos todo o resto explode se sua suposição sobre o estado das portas está errada, mas lock()apenas corrige isso para você? Esqueça que isso torna impossível trancar a porta, o que é absolutamente possível e ocasionalmente útil. O problema aqui é que você misturou duas filosofias diferentes para lidar com suposições incorretas. Isso é confuso. Não faça isso. Não no mesmo nível de abstração com o mesmo estilo de nomenclatura. Ow! Leve uma dessas idéias para fora. Os métodos de serviço na porta devem funcionar da mesma maneira.
Quanto à violação da Separação de Consulta de Comando, não devo tentar fechar uma porta para descobrir se está fechada ou aberta. Eu deveria ser capaz de perguntar. O serviço de porta não fornece uma maneira de fazer isso sem alterar possivelmente o estado da porta. Isso piora muito os comandos que também retornam valores (um mal-entendido comum sobre o que é o CQS). Aqui, comandos de alteração de estado são a única maneira de fazer consultas! Ow!
Quanto às exceções serem mais caras que os códigos de status, isso é conversa sobre otimização. Rápido o suficiente é rápido o suficiente. Não, o verdadeiro problema é que os humanos não esperam exceções para casos típicos. Você pode discutir sobre o que é típico, tudo que você gosta. Para mim, a grande questão é quão legível você está criando o 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, não me faça escrever um código como este. Por favor, nos dê isLocked()e isClosed()métodos. Com quem eu posso escrever meus próprios ensureClosed()e ensureUnlocked()métodos fáceis de ler. Aqueles que só são lançados se suas condições de postagem forem violadas. Prefiro apenas descobrir que você já escreveu e testou, é claro. Apenas não os misture com os que jogam quando não podem mudar de estado. No mínimo, dê a eles nomes distintos.
Faça o que fizer, por favor, não ligue para nada tryClose(). Esse é um nome terrível.
Quanto DoorLockedExceptionsozinho vs também ter DoorAlreadyLockedExceptionmal dizer isso: é tudo sobre o uso de código. Por favor, não projete serviços como esse sem escrever o código de uso e ver a bagunça que você está criando. Refatorar e redesenhar até que o código de uso seja pelo menos legível. De fato, considere escrever o código usando primeiro.
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();
}
}