Pergunta

Eu estou preso decidir como lidar com exceções em minha aplicação.

Muito se meus problemas com exceções vem 1) acessar dados através de um serviço remoto ou 2) desserializar um objeto JSON. Infelizmente eu não posso garantir o sucesso de qualquer uma destas tarefas (conexão corte de rede, malformado objeto JSON que está fora do meu controle).

Como resultado, se eu fizer encontrar uma exceção I simplesmente pegá-lo dentro da função e retorno FALSE para o chamador. Minha lógica é que todo o chamador realmente se preocupa é se a tarefa foi bem-sucedido, não porque ele é não foi bem sucedido.

Aqui está um código de exemplo (em JAVA) de um método típico)

public boolean doSomething(Object p_somthingToDoOn)
{
    boolean result = false;

    try{
        // if dirty object then clean
        doactualStuffOnObject(p_jsonObject);

        //assume success (no exception thrown)
        result = true;
    }
    catch(Exception Ex)
    {
        //don't care about exceptions
        Ex.printStackTrace();
    }
    return result;
}

Penso que esta abordagem é muito bem, mas eu estou muito curioso para saber o que são as melhores práticas para o gerenciamento de exceções (que eu deveria realmente bolha uma exceção todo o caminho até uma pilha de chamadas?).

Em resumo das questões-chave:

  1. Está tudo bem para apenas exceções de captura, mas não bubble-los ou formalmente notificar o sistema (quer através de um registro ou uma notificação para o utilizador)?
  2. Quais as melhores práticas estão lá para exceções que não resultam em tudo o que exigem um bloco try / catch?

Follow Up / Editar

Obrigado por todos os comentários, encontramos algumas excelentes fontes de gerenciamento de exceções on-line:

Parece que a gestão de exceção é uma daquelas coisas que variam com base no contexto. Mas o mais importante, deve ser consistente na forma como eles gerenciar exceções dentro de um sistema.

Além disso atente para código-rot via excessivas try / capturas ou não dar uma exceção seu respeito (uma exceção é alertar o sistema, quais são as necessidades mais para ser avisado?).

Além disso, este é um comentário escolha muito de m3rLinEz .

Eu tendo a concordar com Anders Hejlsberg e que apenas os mais chamadores importa se a operação for bem sucedida ou não.

A partir deste comentário que traz à tona algumas perguntas para pensar sobre quando se lida com exceções:

  • O que é o ponto desta exceção sendo lançada?
  • Como isso faz sentido para lidar com isso?
  • Será que o chamador realmente se preocupam com a exceção ou eles só se importam se a chamada foi bem sucedida?
  • está forçando um chamador para gerir um potencial graciosa exceção?
  • Você está sendo respeitoso com os idoms da língua?
    • Você realmente precisa retornar uma bandeira sucesso como boolean? Retornando boolean (ou um int) é mais de uma mentalidade C do que um Java (em Java você teria apenas que lidar com a exceção) um.
    • Siga as construções de gerenciamento de erro associadas à linguagem :)!
Foi útil?

Solução

Parece estranho para mim que você quer capturar exceções e transformá-los em códigos de erro. Porque você acha que o chamador prefere códigos de erro mais exceções quando este é o padrão em Java e C #?

Quanto a suas perguntas:

  1. Você só deve capturar exceções que você pode realmente manipular. Somente capturar exceções não é a coisa certa a fazer na maioria dos casos. Existem poucas excepções (por exemplo, log e triagem excepções entre segmentos), mas mesmo para os casos você deve geralmente relançar as exceções.
  2. Você definitivamente não deve ter um monte de declarações try / catch na sua código. Novamente, a idéia é apenas para capturar exceções você pode manipular. Você pode incluir um manipulador de exceção de nível superior para transformar qualquer não manipulado exceções em algo pouco útil para o usuário final, mas caso contrário, você não deve tentar capturar cada exceção em todos os lugares possíveis.

Outras dicas

Esta depende da aplicação e da situação. Se a sua construção de um componente de biblioteca, você deve borbulhar exceções, embora eles devem ser embrulhados para ser contextual com o seu componente. Por exemplo, se a sua construção de um banco de dados XML e vamos dizer que você está usando o sistema de arquivos para armazenar seus dados, e você estiver usando as permissões do sistema de arquivos para proteger os dados. Você não gostaria que a borbulhar uma exceção FileIOAccessDenied como que vaza a sua implementação. Em vez disso você iria quebrar a exceção e lançar um erro AccessDenied. Isto é especialmente verdadeiro se você distribuir o componente de terceiros.

Como para se está tudo bem para engolir exceções. Isso depende de seu sistema. Se o seu aplicativo pode lidar com os casos de falha e não há nenhum benefício a partir de notificar o usuário por que falhou, em seguida, vá em frente, embora eu recomendo que o seu registo de fracasso. Eu sempre achei frustrante ser chamado para ajudar a resolver um problema e descobrir que eles foram engolir a exceção (ou substituindo-o e jogando um novo lugar sem definir a exceção interna).

Em geral eu uso as seguintes regras:

  1. Em meus componentes e bibliotecas eu só pegar uma exceção se eu pretendo segurá-lo ou fazer algo baseado nele. Ou se eu quiser fornecer informações contextuais em uma exceção.
  2. Eu uso um try catch geral no ponto de entrada do aplicativo, ou o mais alto nível possível. Se uma exceção fica aqui eu apenas registrá-lo e deixá-lo falhar. Idealmente exceções nunca deve chegar aqui.

Eu acho o seguinte código para ser um cheiro:

try
{
    //do something
}
catch(Exception)
{
   throw;
}

código como este serve nenhum ponto e não devem ser incluídos.

Gostaria de recomendar outra boa fonte sobre o tema. É uma entrevista com inventores de C # e Java, Anders Hejlsberg e James Gosling, respectivamente, sobre o tema da exceção verificada de Java.

Fracasso e Exceções

Há também grandes recursos na parte inferior da página.

Eu tendo a concordar com Anders Hejlsberg e que a maioria dos chamadores só se preocupam se a operação for bem sucedida ou não.

Bill Venners : Você mencionou escalabilidade e preocupações de versionamento em relação ao exceções verificadas. Você poderia esclarecer o que entende por essas duas questões?

Anders Hejlsberg : Vamos começar com versionamento, porque as questões são muito fácil ver lá. Vamos dizer que eu criar um foo método que declara inicia excepções A, B, e C. Em versão dois do foo, eu quero adicionar um monte de recursos, e agora foo poder excepção lance D. É uma quebra mudança para mim para adicionar D para os lances cláusula de que método, porque chamador existente de que a vontade método quase certamente não lidar com isso exceção.

Como adicionar uma nova exceção a uma lança cláusula de um novo cliente breaks versão código. É como a adição de um método a uma interface. Depois de publicar um interface, é para todos prático propósitos imutáveis, porque qualquer implementação do mesmo pode ter a métodos que você deseja adicionar na próxima versão. Então você tem que criar uma nova interface em vez disso. similarmente com exceções, você quer ter para criar um método totalmente novo chamado foo2 que lança mais exceções, ou você teria que pegar exceção D em o novo foo, e transformar o D em um A, B, ou C.

Bill Venners : Mas você não está quebrando seu código nesse caso qualquer maneira, mesmo em uma linguagem sem verificada exceções? Se a nova versão do foo vai lançar uma nova exceção que clientes devem pensar sobre o manuseio, não é o seu código quebrado apenas pela fato de que eles não esperavam que exceção quando escreveram o código?

Anders Hejlsberg : Não, porque em um monte dos casos, as pessoas não se importam. Eles estão não vai lidar com qualquer um destes exceções. Há um nível inferior manipulador de exceção em torno de sua mensagem ciclo. Esse manipulador é apenas vai abrir um diálogo que diz o que deu errado e continuar. os programadores proteger seu código escrevendo try finalmente está em todo lugar, então eles vão voltar corretamente se ocorrer uma exceção, mas eles não estão realmente interessados ??no lidar com as exceções.

A cláusula throws, pelo menos da maneira ele é implementado em Java, não faz necessariamente forçá-lo a lidar com a exceções, mas se você não lidar com eles, o obriga a reconhecer precisamente o que exceções podem passar através. Ela exige que você quer captura declarada exceções ou colocá-los em sua própria cláusula throws. Trabalhar em torno desta exigência, as pessoas fazem coisas ridículas. Por exemplo, eles decorar cada método com ", atira Exceção." Isso apenas completamente derrota o recurso, e você acabou de fazer a escrita programador mais gobbledy lamaçal. Isso não ajuda ninguém.

Editar: Adicionado mais detalhes sobre a converstaion

As exceções verificadas são uma questão controversa em geral, e em Java em particular (mais tarde eu vou tentar encontrar alguns exemplos para aqueles a favor e se opuseram a eles).

Como regras de ouro, tratamento de exceção deve ser algo em torno dessas diretrizes, em nenhuma ordem particular:

  • Por uma questão de manutenção, exceções sempre log de modo que quando você começar a ver os erros, o log vai ajudar na apontando para o lugar seu erro foi provavelmente começou. Nunca deixe printStackTrace() ou os gostos dele, as chances são de um de seus usuários receberão um desses rastreamentos de pilha, eventualmente, e têm exatamente zero conhecimento , como o que fazer com ele.
  • exceções captura você pode segurar, e somente aqueles, e tratá-los , não apenas jogá-los até a pilha.
  • Sempre pegar uma classe de exceção específico e, geralmente, você nunca deve pegar tipo Exception, você é muito provável que engolir exceções caso contrário importantes.
  • Never (nunca) captura Errors !! , ou seja: Throwables Nunca captura como Errors são subclasses deste último. Errors são problemas que você provavelmente nunca será capaz de lidar com (por exemplo OutOfMemory ou outros problemas JVM)

No que diz respeito o seu caso específico, certifique-se de que qualquer cliente chamando seu método irá receber o valor de retorno adequado. Se algo falhar, um método retornando boolean pode retornar falso, mas certifique-se os lugares que você chamar esse método é capaz de lidar com isso.

Você só deve capturar as exceções que você pode lidar com eles. Por exemplo, se você está lidando com a leitura através de uma rede e a conexão expira e você recebe uma exceção que você pode tentar novamente. No entanto, se você está lendo em uma rede e obter uma exceção IndexOutOfBounds, você realmente não pode lidar com isso, porque você não (bem, neste caso você não) sabe o que causou isso. Se você estiver indo para retornar falsa ou -1 ou nulo, ter certeza que é para exceções específicas. Eu não quero uma biblioteca que estou usando retornar um falso em uma rede ler quando a exceção lançada é a pilha está sem memória.

As exceções são erros que não fazem parte da execução normal do programa. Dependendo do que o seu programa faz e seus usos (ou seja, um processador de texto vs. um monitor cardíaco) você vai querer fazer coisas diferentes quando se deparar com uma exceção. Eu tenho trabalhado com o código que usa exceções como parte da execução normal e é definitivamente um cheiro de código.

Ex.

try
{
   sendMessage();

   if(message == success)
   {
       doStuff();
   }
   else if(message == failed)
   {
       throw;
   }
}
catch(Exception)
{
    logAndRecover();
}

Este código me faz vomitar. IMO você não deve se recuperar de exceções a menos que seu um programa de crítica. Se suas exceções jogando então as coisas ruins estão acontecendo.

Todas as opções acima parece razoável, e muitas vezes o seu local de trabalho pode ter uma política. Na nossa casa temos definido para tipos de exceção: SystemException (desmarcado) e ApplicationException (marcada).

Nós concordamos que SystemExceptions não são susceptíveis de ser recuperável e BVE tratado uma vez no topo. Para proporcionar ainda mais contexto, os nossos SystemExceptions são exteneded para indicar onde ocorreu, por exemplo, RepositoryException, ServiceEception, etc.

ApplicationExceptions poderia ter negócios significando como InsufficientFundsException e deve ser tratado pelo código do cliente.

Witohut um exemplo concreto, é difícil comentar sobre sua implementação, mas eu nunca iria usar códigos de retorno, eles são um problema de manutenção. Você pode engolir uma exceção, mas você precisa decidir por que, e sempre registrar o evento e stacktrace. Por fim, como o seu método não tem nenhum outro processamento é bastante redundante (exceto para encapsulamento?), Assim doactualStuffOnObject(p_jsonObject); poderia retornar um boolean!

Depois de algum pensamento e olhando para o seu código parece-me que você está simplesmente rethrowing a exceção como um booleano. Você poderia simplesmente deixar o método de passar essa exceção a (você não tem sequer a pegá-lo) e lidar com ele no chamador, já que é o lugar onde importa. Se a exceção fará com que o interlocutor para repetir esta função, o chamador deve ser o único captura a exceção.

Ele pode às vezes acontecer que a exceção que você encontrou não vai fazer sentido para o chamador (ou seja, é uma exceção rede), caso em que você deve envolvê-la em uma exceção específica de domínio.

Se, por outro lado, os sinais de exceção um erro irrecuperável em seu programa (ou seja, o resultado final desta excepção será rescisão programa) Eu pessoalmente gosto de fazer essa explícita pela captura-lo e atirar uma exceção tempo de execução.

Se você estiver indo para usar o padrão de código no seu exemplo, chamá-lo TryDoSomething e capturar exceções única específicos.

Além disso considerar usar um exceção Filtro quando o log exceções para fins de diagnóstico. VB tem suporte ao idioma para os filtros de exceção. O link para o blog do Greggm tem uma implementação que podem ser utilizados a partir de C #. filtros de exceção têm melhores propriedades para debuggability mais de captura e relançar. Especificamente, você pode registrar o problema no filtro e deixe a exceção continuar a propagar. Esse método permite um anexar um JIT (Just in Time) depurador de ter a pilha original completo. A rethrow corta a pilha fora do ponto em que foi relançada.

Os casos em que TryXXXX faz sentido é quando você está enrolando uma função terceiros que joga em casos que não são verdadeiramente excepcional, ou são simples difícil teste sem chamar a função. Um exemplo seria algo como:

// throws NumberNotHexidecimalException
int ParseHexidecimal(string numberToParse); 

bool TryParseHexidecimal(string numberToParse, out int parsedInt)
{
     try
     {
         parsedInt = ParseHexidecimal(numberToParse);
         return true;
     }
     catch(NumberNotHexidecimalException ex)
     {
         parsedInt = 0;
         return false;
     }
     catch(Exception ex)
     {
         // Implement the error policy for unexpected exceptions:
         // log a callstack, assert if a debugger is attached etc.
         LogRetailAssert(ex);
         // rethrow the exception
         // The downside is that a JIT debugger will have the next
         // line as the place that threw the exception, rather than
         // the original location further down the stack.
         throw;
         // A better practice is to use an exception filter here.
         // see the link to Exception Filter Inject above
         // http://code.msdn.microsoft.com/ExceptionFilterInjct
     }
}

Se você usa um padrão como TryXXX ou não é mais uma questão de estilo. A questão da captura todas as exceções e engoli-los não é uma questão de estilo. Certifique-se de exceções inesperadas estão autorizados a propagar!

Eu sugiro dar suas sugestões a partir da biblioteca padrão para o idioma que você está usando. Eu não posso falar para C #, mas vamos olhar para Java.

Por exemplo java.lang.reflect.Array tem um método set estática:

static void set(Object array, int index, Object value);

A forma C seria

static int set(Object array, int index, Object value);

... com o valor de retorno ser um indicador de sucesso. Mas você não está em C mundo mais.

Uma vez que você abraçar exceções, você deve achar que ele faz o seu código mais simples e mais clara, movendo o código de manipulação de erro longe de sua lógica de núcleo. Aim ter lotes de declarações em um único bloco try.

Como outros - você deve ser o mais específico possível no tipo de exceção que você pegar.

Se você estiver indo para capturar uma exceção e retorno falso, que deveria ser uma exceção muito específico. Você não está fazendo isso, você está pegando todos eles e retornar false. Se eu conseguir uma MyCarIsOnFireException eu quero saber sobre isso imediatamente! O resto dos Exceções eu poderia não se preocupam. Então você deve ter uma pilha de manipuladores de exceção que dizem "whoa whoa algo está errado aqui" para algumas exceções (rethrow, ou captura e relançar uma nova exceção que explica melhor o que aconteceu) e apenas retornar false para os outros.

Se este é um produto que você vai ser o lançamento deve ser login essas exceções em algum lugar, ele irá ajudá-lo a ajustar as coisas no futuro.

Edit: Quanto à questão de tudo embrulho em um try / catch, acho que a resposta é sim. Exceções devem ser tão rara em seu código que o código nas executa bloco catch tão raramente que não atingiu o desempenho em tudo. Uma exceção deve ser um estado onde a sua máquina de estado quebrou e não sabe o que fazer. Pelo menos relançar uma exceção que explica o que estava acontecendo no momento e tem o interior exceção capturada dele. "Exceção no método doSomeStuff ()" não é muito útil para quem tem de descobrir por que ele quebrou enquanto você está de férias (ou em um novo emprego).

A minha estratégia:

Se a função original retornou vazio eu mudá-lo para retornar bool . Se exceção / erro ocorreu retorno false , se tudo estava retorno bem true .

Se a função deve retornar alguma coisa, então quando exceção / erro ocorreu retorno nulo , caso contrário, o item retornável.

Em vez de bool a string poderia ser devolvido contendo a descrição do erro.

Em todos os casos, antes de retornar log nada o erro.

Algumas respostas excelentes aqui. Eu gostaria de acrescentar que, se você acabar com algo parecido com você postou, pelo menos imprimir mais do que o rastreamento de pilha. Diga o que você estava fazendo no momento, e Ex.getMessage (), para dar o desenvolvedor uma chance de lutar.

blocos try / catch formar um segundo conjunto de lógica embutida sobre o primeiro conjunto (principal), como tal, eles são uma ótima maneira de libra fora ilegível, difícil de depurar código espaguete.

Ainda assim, usado razoavelmente eles trabalham maravilhas em legibilidade, mas você deve apenas seguir duas regras simples:

  • usá-los (com moderação) no baixo nível para biblioteca de captura lidar com questões, e transmiti-los de volta para o principal fluxo lógico. A maior parte da manipulação de erro que queremos, deve ser proveniente do próprio código, como parte dos dados em si. Por que fazer condições especiais, se os dados retornando não é especial?

  • Use um grande manipulador no nível superior para gerenciar qualquer ou todas as condições estranhas que surgem no código que não são apanhados em um nível baixo. Faça algo útil com os erros (logs, reinicia, recuperações, etc).

Além destes dois tipos de tratamento de erros, todo o resto do código no meio deve ser livre e clara dos objetos try / código de captura e de erro. Dessa forma, ele funciona de forma simples e como esperado, não importa onde você usá-lo, ou o que você faz com ele.

Paul.

Eu posso ser um pouco tarde com a resposta, mas o tratamento de erros é algo que podemos sempre mudar e evoluir ao longo do tempo. Se você quer ler algo mais sobre este assunto eu escrevi um post no meu novo blog sobre isso. http://taoofdevelopment.wordpress.com

Happy codificação.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top