Eu sempre me pego lutando com isso ... tentando encontrar o equilíbrio certo entre tentar / capturar e o código não se tornar essa bagunça obscena de guias, colchetes e exceções sendo lançadas de volta na pilha de chamadas como uma batata quente. Por exemplo, eu tenho um aplicativo que estou desenvolvendo agora que usa SQLite. Eu tenho uma interface de banco de dados que abstrai as chamadas SQLite e um modelo que aceita coisas para entrar / sair do banco de dados ... Portanto, se / quando ocorrer uma exceção SQLite, ela deverá ser lançada no modelo (que a chamou) ), que deve distribuí-lo a quem chamou o AddRecord / DeleteRecord / o que for ...
Sou fã de exceções, em vez de retornar códigos de erro, porque eles podem ser ignorados, esquecidos etc., enquanto uma exceção precisa ser tratada (concedida, eu poderia pegar e seguir imediatamente ...) certo de que deve haver uma maneira melhor do que o que estou acontecendo agora.
Edit: Eu deveria ter formulado isso um pouco diferente. Eu entendo re-jogar como tipos diferentes e tal, eu disse isso muito mal e isso é culpa minha. Minha pergunta é ... como é melhor manter o código limpo ao fazer isso? Começa a parecer extremamente confuso para mim depois de um tempo.
fonte
Respostas:
Pense nisso em termos de digitação forte, mesmo se você não estiver usando uma linguagem fortemente tipada - se o seu método não puder retornar o tipo que você esperava, deve estar lançando uma exceção.
Além disso, em vez de lançar o SQLException até o modelo (ou pior, interface do usuário), cada camada deve capturar exceções conhecidas e agrupar / alterar / substituir por exceções adequadas a essa camada:
Isso deve ajudar a limitar o número de exceções que você está procurando em cada camada e a manter um sistema de exceção organizado.
fonte
throws SQLException
um método que não implica que o SQL esteja envolvido. E o que acontece se você decidir que algumas operações devem ir para um armazenamento de arquivos? Agora você tem que declararthrows SQLException, IOException
, etc. Isso ficará fora de controle.As exceções permitem escrever código mais limpo, porque a maior parte dele cuida do caso normal, e os casos excepcionais podem ser tratados posteriormente, mesmo em um contexto diferente.
A regra para lidar com exceções (captura) é que ela deve ser feita por contexto que possa realmente fazer algo sobre isso. Mas isso tem uma exceção:
As exceções devem ser capturadas nos limites do módulo (especialmente os limites da camada) e mesmo que apenas para incluí-las e lançar uma exceção de nível superior que tenha significado para o chamador. Cada módulo e camada deve ocultar seus detalhes de implementação, mesmo com relação a exceções (um módulo heap pode gerar HeapFull, mas nunca ArrayIndexOutOfBounds).
No seu exemplo, é improvável que as camadas superiores possam fazer algo sobre uma exceção SQLite (se o fizerem, tudo será tão acoplado ao SQLite que você não poderá mudar a camada de dados para outra coisa). Existem várias razões previsíveis para que coisas como Adicionar / Excluir / Atualizar falhem, e algumas delas (alterações incompatíveis nas transações simultâneas) são impossíveis de recuperar, mesmo na camada de dados / persistência (violação das regras de integridade, fi). A camada de persistência deve converter as exceções em algo significativo nos termos da camada de modelo, para que as camadas superiores possam decidir se devem tentar novamente ou falhar normalmente.
fonte
Como regra geral, você deve capturar apenas exceções específicas (por exemplo, IOException) e somente se tiver algo específico a fazer depois de capturar a exceção.
Caso contrário, geralmente é melhor deixar as Exceções borbulharem até a superfície para que possam ser expostas e tratadas. Algumas pessoas chamam isso de falha rápida.
Você deve ter algum tipo de manipulador na raiz do seu aplicativo para capturar Exceções não tratadas que surgiram abaixo. Isso permite que você apresente, relate ou gerencie a exceção de maneira adequada.
O agrupamento de exceções é útil quando você precisa lançar uma exceção em um sistema distribuído e o cliente não tem a definição da falha do servidor.
fonte
Imagine que você está escrevendo uma classe de pilha. Você não coloca nenhum código de manipulação de exceção na classe, pois isso pode produzir as seguintes exceções.
Uma abordagem simplista para agrupar exceções pode decidir agrupar essas duas exceções em uma classe de exceção StackError. No entanto, isso realmente perde o objetivo de agrupar exceções. Se um objeto lança uma exceção de baixo nível, isso significa que o objeto está quebrado. No entanto, há um caso em que isso é aceitável: quando o objeto é de fato quebrado.
O ponto de agrupar exceções é que o objeto deve fornecer exceções apropriadas para erros normais. A pilha deve aumentar StackEmpty e não ArrayIndexError ao saltar de uma pilha vazia. A intenção não é evitar lançar outras exceções se o objeto ou código estiver quebrado.
O que realmente queremos evitar é capturar exceções de baixo nível que foram passadas por objetos de alto nível. Uma classe de pilha que lança um ArrayIndexError ao saltar de uma pilha vazia é um problema menor. Se você realmente pegar esse ArrayIndexError, temos um problema sério. A propagação de erros de baixo nível é um pecado muito menos sério do que pegá-los.
Para trazer isso de volta ao seu exemplo de SQLException: por que você está recebendo SQLExceptions? Um motivo é porque você está passando consultas inválidas. No entanto, se sua camada de acesso a dados estiver gerando consultas incorretas, ela será quebrada. Ele não deve tentar reorganizar sua quebra em uma exceção DataAccessFailure.
No entanto, uma SQLException também pode surgir devido a uma perda de conexão com o banco de dados. Minha estratégia nesse ponto é capturar a exceção na última linha de defesa, relatar ao usuário que a conectividade do banco de dados foi perdida e desligada. Como o aplicativo tem perda de acesso ao banco de dados, não há realmente muito mais a ser feito.
Não sei como é o seu código. Mas parece que você pode traduzir cegamente todas as exceções em exceções de nível superior. Você deve fazer isso apenas em um número relativamente pequeno de casos. A maioria das exceções de nível inferior indica bugs no seu código. Pegá-los e embalá-los novamente é contraproducente.
fonte