Você lança uma exceção de argumento ou exceção de argumento de métodos privados?

20

Acabei de revisar algum código que escrevi há algum tempo e posso ver que tenho alguns métodos particulares que lançam percepções de argumentos de argumento e / ou exceções de argumento se houver problemas com os parâmetros dos métodos.

Eu acho que minha lógica é que ajuda a prova futura do aplicativo se alguém tentar "abusar" do método no futuro. No entanto, dado que é um método privado e as pessoas que provavelmente chamarão esse método podem ver os comentários e o código associados, é apenas desnecessário jogar isso. Certamente não dói tê-los, embora adicione desordem.

Meu sentimento é que essas exceções geralmente são mais úteis em algo como uma API que será exposta publicamente.

Mr Moose
fonte

Respostas:

22

Normalmente, para métodos privados, você não lança exceções, pois, como o escreveu, o desenvolvedor deve saber como e de onde ele está chamando o método. Como tal, as variáveis ​​passadas como parâmetros para o método privado devem ser verificadas fora do método, ou seja, antes de chamá-lo. Lançar "InvalidArgumentException" e outras exceções é considerado uma boa prática para métodos públicos (esteja você escrevendo uma "API" ou não).

Para os casos em que você deseja lançar "InvalidArgumentException", vale mencionar que há uma Assertclasse na API Spring para Java desde a versão 1.1.2. Foi muito útil - pelo menos para mim - escrever menos código para realizar verificações.

No entanto, você pode usar "asserts" para verificar parâmetros em métodos particulares. Esse é um dos seus verdadeiros propósitos. São mais motivos para usá-los, confira o link a seguir, que também explica detalhadamente quando usar afirmações e quando usar exceções. As declarações não devem ser incluídas no código de produção e o compilador as remove por padrão. Então, eles são o que você está procurando: ajudando os desenvolvedores, invisíveis para os usuários. Em Java, você precisa usar um sinalizador especial ("-ea") para informar ao compilador para habilitar asserções. Você pode considerá-los amigos de "depuração".

Aqui está como usar as declarações em:

Jalayn
fonte
1
O Apache Commons também possui uma classe Validate que funciona de maneira semelhante à classe Assert da Spring
Rosa Richter
@cantido sim e eu também acrescentaria que a classe Preconditions na Guava do Google, que funciona da mesma maneira. Obrigado pela informação :-)
Jalayn
2

Como tudo o mais, depende ....

Se os métodos públicos forem invólucros simples que chamam o método privado (na linha de um método sobrecarregado privado), pode fazer sentido lançar uma exceção no método privado em vez de verificar cada um deles.

Geralmente, se ele não atender à definição acima, normalmente eu não verificaria os argumentos / lançaria uma exceção em um método privado. Embora existam outros casos, geralmente faço isso em um método privado antes de executar alguma operação cara que poderia falhar parcialmente se os argumentos forem inválidos.

Ken Henderson
fonte
2

Percebo que, embora a pergunta não tenha um rótulo de idioma, provavelmente está falando implicitamente sobre "idiomas do café". Mas apenas por uma questão de integridade, gostaria de mencionar o consenso aparente um tanto divergente no mundo C ++.

Normalmente, há três coisas em que os programadores de C ++ estarão interessados:

  • Ele terá zero de sobrecarga nas compilações otimizadas? (Ou seja, pode ser "compilado"?)
  • Posso usá-lo para interceptar um depurador no ponto em que o erro foi detectado?
  • Posso usá-lo para relatar problemas de funções declaradas noexcept?

No passado, eu abordei o primeiro problema escrevendo código como este

int
factorial(const int n)
{
  if (CHECK_ARGS)
    {
      if (n < 0)
        throw std::invalid_argument {"n < 0"};
    }
  int fac = 1;
  for (int i = 2; i <= n; ++i)
    fac *= i;
  return fac;
}

em que d CHECK_ARGSé #defineuma constante em tempo de compilação para que o compilador possa eliminar completamente todo o código de verificação de argumentos em compilações otimizadas. (Não estou dizendo que compilar as verificações é uma coisa boa em geral, mas acredito que um usuário deve ter a opção de compilá-las.)

Ainda gosto dessa solução: o código de verificação de argumentos é claramente visível, agrupado no if. No entanto, o segundo e o terceiro problema não são resolvidos por isso. Portanto, agora estou mais inclinado a usar uma assertmacro para verificação de argumentos.

Os padrões de codificação Boost concordam com isso:

E os erros do programador?

Como desenvolvedor, se violei uma pré-condição de uma biblioteca que estou usando, não quero que a pilha seja desenrolada. O que eu quero é um dump principal ou equivalente - uma maneira de inspecionar o estado do programa no ponto exato em que o problema foi detectado. Isso geralmente significa assert()ou algo parecido.

Houve uma palestra muito interessante proferida por John Lakos no CppCon'14 intitulada Programação Defensiva Feita Corretamente ( parte 1 , parte 2 ). Na primeira parte de sua palestra, ele discute a teoria dos contratos e o comportamento indefinido. Na segunda parte, ele apresenta o que considero uma proposta muito boa para verificação sistemática de argumentos. Em essência, ele propõe macros de asserção que permitem ao usuário selecionar quanto de orçamento (em termos de utilização da CPU) ela deseja doar à biblioteca para verificação de argumentos e fazer com que a biblioteca faça uso racional desse orçamento. Além disso, o usuário também pode instalar uma função global de tratamento de erros que será chamada caso um contrato quebrado seja detectado.

Quanto ao aspecto de uma função ser privada, não creio que isso signifique que nunca deveremos verificar se seus argumentos. Podemos confiar mais em nosso próprio código para não violar o contrato de uma função interna, mas também sabemos que também não somos perfeitos. A verificação de argumentos nas funções internas é tão útil para detectar nossos próprios erros quanto nas funções públicas para detectar erros no código do cliente.

5gon12eder
fonte
1

Considere a seguinte estrutura:

  1. Lógica interna: Esta função assume que é chamada com parâmetros corretos e, portanto, utiliza assertivas para verificar pré-condições, pós-condições e invariantes para verificar sua lógica interna.

  2. Interface de usuário de fardos: Esta função envolve o funcionamento interno e usa InvalidArgumentExceptions para lidar com valores errados e dizer ao usuário para corrigir suas entradas: Assert(x).hasLength(4);, Assume(y).isAlphanumeric();, Assert(z).isZipCode();, Assume(mailAdress).matchesRegex(regex_MailAdress);, Reject(x).ifEmpty();, etc.

  3. Wrapper de interface em lote: Esta função agrupa a função interna e usa log, marcações de validade e estatísticas para manipular valores errados sem interromper alguma tarefa de longa execução. As marcações poderiam ser usadas mais tarde por alguém verificando e limpando o banco de dados de resultados.

  4. Wrapper da interface da linha de comandos: esta função agrupa a função interna e solicita novamente a última entrada.

Você deve usar ambos - afirmações e exceções - em métodos diferentes para tarefas diferentes. Você deve separar a lógica interna da verificação de parâmetros. Compare-o com a separação de Model, View, Controller.

Ralf K.
fonte
0

Existem maneiras melhores de evitar a verificação de referência nula: use o contrato de código ou uma estrutura de AOP para fazer a verificação. Google "contrato de código c #" ou "postsharp".

Codism
fonte
Acho que minha pergunta se estenderia também aos contratos de código. Existe a necessidade de verificar as pré-condições do método em um método particular (por exemplo, para provas futuras ou impedir que você atire no próprio pé)?
Moose