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 DoorLockedException
sozinho vs também ter DoorAlreadyLockedException
mal 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();
}
}