Exceções "Erro de programação" - Minha abordagem é sólida?


9

Atualmente, estou tentando melhorar meu uso de exceções e encontrei a importante distinção entre exceções que significam erros de programação (por exemplo, alguém passou nulo como argumento ou chamou um método em um objeto depois que ele foi descartado) e aqueles que significam uma falha no operação que não é culpa do chamador (por exemplo, uma exceção de E / S).

Como esses dois tipos de exceções devem ser tratados de maneira diferente? Você acha que as exceções de erro precisam ser explicitamente documentadas ou é suficiente para documentar as pré-condições relevantes? E você pode deixar de fora a documentação de uma exceção de pré-condição ou erro, se for óbvio (por exemplo, ObjectDisposedExceptionao chamar um método em um objeto descartado)


Normalmente, distingo outra categoria de exceções: exceções de negócios. Referem-se a condições de erro nas quais o usuário está interessado nos detalhes. Normalmente, faço essas exceções verificadas.
beluchin

Respostas:


2

Eu acho que você está no caminho certo. Nem jogar, capturar nem documentar todas as exceções potencialmente lançáveis ​​faz muito sentido. Há momentos em que o rigor do produto exige um alto grau de emprego e documentação de exceção (por exemplo, certos aspectos críticos de segurança de um sistema).

A estratégia de ser mais defensivo, usando conceitos de contrato para identificar condições prévias (e pós-condições) em chamadores especialmente a jusante (por exemplo, qualquer coisa que se assemelhe a um membro público ou protegido) será frequentemente mais eficaz e mais flexível. Isso se aplica não apenas à implementação, mas à documentação. Se os desenvolvedores souberem o que é esperado, é mais provável que eles sigam as regras e menos propensos a confundir ou abusar do código que você escreveu.

Algumas das coisas comuns que devem ser documentadas incluem o caso de parâmetros nulos. Muitas vezes, há uma consequência para o uso deles que leva o resultado a algo que normalmente não seria esperado, mas que é permitido e usado por várias razões, às vezes por flexibilidade. Como consumidor de um membro que possui parâmetros que permitem valores nulos ou outros valores não racionais especiais (como tempo negativo ou quantidades negativas), espero vê-los identificados e explicados.

Para parâmetros não nulos, como consumidor de um membro público ou protegido, quero saber que nulo não é permitido. Quero saber qual é o intervalo válido de valores no contexto fornecido. Quero saber as conseqüências do uso de valores que estão fora do intervalo normal, mas que são válidos em um contexto de chamada diferente (por exemplo, o valor do tipo geralmente é válido para qualquer operação, mas não aqui - como um parâmetro booleano que não espere falso como um valor válido.

No que diz respeito à plataforma ou às interfaces bem conhecidas, não acho que você precise ir ao extremo ao documentá-lo. No entanto, como você tem a oportunidade, como desenvolvedor, de variar a implementação de qualquer orientação da plataforma, observe como isso pode ser útil.

Específicas para IDisposable, as implementações dessa interface geralmente oferecem um método alternativo preferido ao processo de descarte explícito. Nesses casos, destaque o método preferido e observe que a eliminação explícita não é preferida.


Entendo a necessidade de documentar quais valores são permitidos, mas se você performReadCommand(ICommand cmd, int replySizeBytes)vir uma função - você esperaria que nulo fosse aceitável para cmd ou um valor negativo para replySizeBytes? A documentação da IMO seria necessária apenas se esses valores realmente fossem permitidos, e você provavelmente deve saber se 0 é válido para replySizeBytes.
Medo42

Ambos podem ser "válidos", dependendo da implementação. Você é ou eu, e nem todos aqui esperariam que valores nulos ou negativos fossem apropriados para essa assinatura, mas poderiam ser. Considere um ambiente em que o leitor seja um processo persistente e bloqueador e, quando o cmd for nulo, pode significar não alterar o comando usado pelo leitor e continue com a próxima leitura usando o comando anterior. Idealmente, nesse caso, uma assinatura de método mais apropriada que omitisse o cmd como parâmetro seria definida e usada, mas talvez não.
23812 JustinC

2

Aqui estão meus próprios pensamentos. Observe que não tenho muita certeza de que esse é o melhor caminho a seguir, e foi por isso que criei essa pergunta em primeiro lugar.

Tanto quanto eu entendo, faz pouco sentido para um chamador imediato realmente lidar com exceções de erro de programação, ele deve garantir que as pré-condições sejam atendidas. Somente os manipuladores de exceção "externos" nos limites da tarefa devem capturá-los, para que possam manter o sistema em execução se uma tarefa falhar.

Para garantir que o código do cliente possa capturar de maneira limpa as exceções de "falha" sem capturar exceções de erro por engano, crio minhas próprias classes de exceção para todas as exceções de falha agora e as documento nos métodos que as lançam. Eu os faria exceções verificadas em Java.

Até recentemente, eu tentava documentar todas as exceções que um método poderia lançar, mas que às vezes cria uma lista indesejada que precisa ser documentada em todos os métodos da cadeia de chamadas até que você possa mostrar que o erro não ocorrerá. Em vez disso, agora documento as condições prévias nas descrições de resumo / parâmetro e nem mencione o que acontece se elas não forem atendidas. A idéia é que as pessoas não devem tentar capturar essas exceções explicitamente de qualquer maneira, portanto, não há necessidade de documentar seus tipos.

Para documentar as pré-condições, declarar o óbvio apenas cria confusão desnecessária - se passar nulo a um método não faz sentido óbvio, o chamador deve esperar uma exceção se passar nulo de qualquer maneira, mesmo que isso não esteja documentado. O mesmo vale para o caso de ObjectDisposedException - essa interface é tão amplamente usada que alguém que chama Dispose ficaria ciente da responsabilidade de garantir que ninguém continuasse usando o objeto.


1

Uma regra prática razoável:

  1. Pegue qualquer exceção que você possa corrigir.
  2. Capte, comente e repita qualquer exceção que você não pode corrigir, mas pode dizer algo útil.
  3. Não pegue nenhuma exceção que não possa processar ou diagnosticar melhor do que o processamento padrão.

Você pode querer ter um manipulador de exceção não fatal de nível superior para interceptar qualquer coisa que não possa ser tratada abaixo para tentar impedir que seu aplicativo caia, mas isso dependerá fortemente do seu aplicativo em particular. Por exemplo, os aplicativos iOS preferem interceptar o máximo possível; um aplicativo de linha de comando pode estar perfeitamente bem se não detectar quase nenhuma exceção.


0

Mesmo Java não "documenta" todas as exceções. Apesar do requisito de que todas as thrown exceções sejam mencionadas em uma throwscláusula, qualquer linha de código pode gerar um RuntimeExceptionsem a necessidade de declará-la na assinatura do método.


Isso contraria o conselho popular - especificamente, o Java efetivo, 2ª ed., Item 62 é "Documente todas as exceções lançadas por cada método". De fato, Bloch reconhece que exceções desmarcadas geralmente são para erros de programação, mas que devem ser documentadas com cuidado também, pois essa é "a melhor maneira" de documentar as pré-condições de um método. Não tenho certeza se concordo. Se documentar as execuções, as faço parte do contrato de interface e talvez seja necessário adicionar código para mantê-las iguais se minha implementação for alterada.
Medo42

11
Para todo especialista, existe um contra-especialista. Bruce Eckel, autor do famoso Thinking in Java , esteve no campo de exceções pro-verificadas e mudou de lado. Há uma boa discussão entre Eckels e Anders Hejlsberg , o criador do C #, que não verificou exceções.
Ross Patterson

0

A maneira padrão de fazer isso é com a abordagem java. Erros de programação devem ser exceções desmarcadas e não devem ser capturados para garantir uma falha rápida. Erros de contrato são exceções verificadas e devem ser tratadas adequadamente pelo cliente.


2
Você considera (por exemplo) um valor calculado (ou um valor de erro de) NULL como um erro de programação ou um erro de contrato ? I ir com a sua distinção, mas o limite não é tão clara :)
Andrew

Se o nulo for calculado a partir de um argumento que foi passado (como um argumento nulo ou algo inválido), isso é um erro de contrato. Se o nulo for calculado devido a um código incorreto, isso é um erro de programação. Se o nulo deve ser calculado, não é um erro. Realmente não vejo nenhuma ambiguidade neste exemplo.
J.Ashworth
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.