Digamos que eu tenho Door
s que são gerenciados por a DoorService
. Ele DoorService
é responsável por abrir, fechar e trancar as portas armazenadas no banco de dados.
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 o método lock, existe um DoorAlreadyLockedException
e, para o método aberto, existe um DoorLockedException
. Esta é uma opção, mas existem outras opções possíveis:
1) Use DoorLockedException
para tudo, é um pouco estranho ao capturar a exceção em uma lock()
chamada
try
{
doorService.lock(myDoor);
}
catch(DoorLockedException ex) // door ALREADY locked
{
//error handling...
}
2) Tenha os 2 tipos de exceções DoorLockedException
eDoorAlreadyLockedException
3) Tenha os 2 tipos de exceções, mas deixe DoorAlreadyLockedException extends DoorLockedException
Qual é a melhor solução e por quê?
java
exceptions
class-design
Inverno
fonte
fonte
Respostas:
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:
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.
Por favor, não me faça escrever um código como este. Por favor, nos dê
isLocked()
eisClosed()
métodos. Com quem eu posso escrever meus própriosensureClosed()
eensureUnlocked()
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 terDoorAlreadyLockedException
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.fonte
lock()
método que está chamandoclose()
era apenas eu sendo preguiçoso ao escrever a pergunta, mas obrigado por me informar disso! Não vou gostar disso ao escrever código real. No meu caso, os três métodos nunca são realmente chamados sucessivamente. É apenas: chame um, se não houver uma exceção boa, mostre uma caixa de diálogo traduzida e possivelmente faça logout (se InvalidTokenException). Eu tenhoisLocked()
eisOpen()
métodos e eles são usados no fluxo de controle do servidor, mas não devem ser usados como uma maneira de verificar se uma caixa de diálogo de erro deve ser mostrada; esse é um trabalho para uma exceção.DoorAlreadyLockedException
mas cria classes gêmeas.public class DoorAlreadyLockedException inherits DoorLockedException {}
. Defina uma classe em uma linha apenas para lhe dar um bom nome. Escolha o que você herdou sabiamente. Prefiro herdar o mais alto possível, sem ser desnecessariamente redundante, para evitar uma longa cadeia de herança.NoOneGaveMeAKey
deNoOneGaveMeAKeyException
apenas para ser pendente. Caso contrário, eu concordo com o ponto principal, que é o de que antes do processamento, precisamos saber o estado do objeto.isOpen
/isClosed
não é bom o suficiente. Pense no sistema de arquivos. Você pode verificar se o arquivo existe / não existe, mas isso ainda pode mudar quando você tentar lê-lo ou gravá-lo, eIOException
isso resultará. Talvez, nesse contexto, haja realmente a chance de o estado ser inválido quando você abre ou fecha a porta.Fazer uma distinção
DoorLockedException
eDoorAlreadyLockedException
sugerir que diferentes tipos de exceção estão sendo usados para controle de fluxo, uma prática que já é amplamente considerada um antipadrão.Ter vários tipos de exceção para algo tão benigno é gratuito. A complexidade adicional não é compensada pelos benefícios adicionais.
Basta usar
DoorLockedException
para tudo.Melhor ainda, simplesmente não faça nada. Se a porta já estiver trancada, ela ainda estará no estado correto quando o
lock()
método for concluído. Não crie exceções para condições que não sejam dignas de nota.Se você ainda não se sentir confortável com isso, renomeie seu método
ensureDoorLocked()
.fonte
DoorLockedException
eDoorAlreadyLockedException
sugerir que é apenas ter um manipulador de erros muito mais significativo para olock()
método, ao custo de ter uma classe quase duplicada.O assunto desta pergunta "Os tipos de exceções recicladas devem ser favorecidos em relação aos de uso único?" parece ter sido ignorado, não apenas nas respostas, mas nos detalhes das perguntas (as respostas foram nos detalhes da pergunta).
Concordo com a maioria das críticas ao código que os entrevistados ofereceram.
Mas, como resposta à pergunta principal, eu diria que você deve preferir reutilizar 'exceções recicladas' em vez de 'uso único'.
No uso mais típico do tratamento de exceções, você tem uma grande DISTÂNCIA no código entre o local onde a exceção é detectada e lançada e onde a exceção é capturada e manipulada. Isso significa que o uso de tipos privados (modulares) no tratamento de exceções geralmente não funcionará bem. Um programa típico com manipulação de exceções - terá vários locais de nível superior no código em que as exceções são tratadas. E como esses são locais de nível superior, é difícil para eles terem conhecimento detalhado sobre aspectos estreitos de módulos específicos.
Em vez disso, eles provavelmente terão manipuladores de alto nível para alguns problemas de alto nível.
A única razão pela qual classes de exceção personalizadas detalhadas parece fazer sentido em sua amostra é porque (e aqui estou repetindo os outros entrevistados) - você não deve usar o tratamento de exceções para o fluxo de controle comum.
fonte
Uma alternativa inexplorada. Você pode estar modelando a coisa errada com suas aulas, e se você as tivesse?
Agora é impossível tentar qualquer coisa que seja um erro. Você não precisa de nenhuma exceção.
fonte
Responderei à pergunta original, em vez de criticar o exemplo.
Simplificando, se você espera que o cliente precise reagir de maneira diferente para cada caso, ele garante um novo tipo de exceção.
Se você decidir usar vários tipos de exceção e, no entanto, esperar que, em alguns casos, o cliente queira criar uma cláusula de captura comum para eles, provavelmente crie uma superclasse abstrata que os dois estendem.
fonte
Este é um pseudocódigo, mas acho que você entenderá o que quero dizer:
Sim, a reutilização do tipo de exceção faz sentido, se você usar exceções para coisas realmente excepcionais . Porque coisas verdadeiramente excepcionais são principalmente preocupações transversais. Você estava usando Exceções basicamente para controlar fluxos e isso leva a esses problemas.
fonte
DoorStuckException
eHouseOnFireException
não devem ser verificados, eles não devem ser capturados em todas as chamadas.