Ao programar por contrato, uma função ou método primeiro verifica se suas condições prévias foram cumpridas, antes de começar a trabalhar em suas responsabilidades, certo? As duas formas mais proeminentes para fazer essas verificações são, por assert
e por exception
.
- afirma falhar apenas no modo de depuração. Para garantir que é crucial (unidade) testar todas as condições prévias do contrato para verificar se elas realmente falham.
- exceção falha no modo de depuração e liberação. Isso tem o benefício de que o comportamento de depuração testado é idêntico ao do release, mas incorre em uma penalidade no desempenho do tempo de execução.
Qual você acha que é preferível?
Veja a pergunta repetida aqui
exception
assert
design-by-contract
andreas buykx
fonte
fonte
Respostas:
Desabilitar a declaração nas compilações de versão é como dizer "Nunca terei nenhum problema em uma compilação de versão", o que geralmente não é o caso. Portanto, a afirmação não deve ser desativada em uma compilação de versão. Mas você não quer que a versão seja travada sempre que ocorrerem erros, não é?
Portanto, use exceções e use-as bem. Use uma hierarquia boa e sólida de exceções e garanta que você captura e pode colocar um gancho na exceção lançada no depurador para capturá-la. No modo de liberação, você pode compensar o erro e não uma falha direta. É o caminho mais seguro a seguir.
fonte
A regra geral é que você deve usar asserções ao tentar capturar seus próprios erros e exceções ao tentar capturar os erros de outras pessoas. Em outras palavras, você deve usar exceções para verificar as pré-condições para as funções públicas da API e sempre que obtiver dados externos ao seu sistema. Você deve usar declarações para as funções ou dados internos ao seu sistema.
fonte
O princípio que sigo é o seguinte: Se uma situação puder ser realisticamente evitada pela codificação, use uma asserção. Caso contrário, use uma exceção.
As afirmações são para garantir que o Contrato está sendo respeitado. O contrato deve ser justo, para que o cliente esteja em posição de garantir a conformidade. Por exemplo, você pode declarar em um contrato que um URL deve ser válido porque as regras sobre o que é e o que não é um URL válido são conhecidas e consistentes.
Exceções são para situações que estão fora do controle do cliente e do servidor. Uma exceção significa que algo deu errado e não há nada que possa ter sido feito para evitá-lo. Por exemplo, a conectividade de rede está fora do controle de aplicativos, portanto não há nada que possa ser feito para evitar um erro de rede.
Eu gostaria de acrescentar que a distinção de afirmação / exceção não é realmente a melhor maneira de pensar sobre isso. O que você realmente quer pensar é o contrato e como ele pode ser cumprido. No meu exemplo de URL acima, a melhor coisa a fazer é ter uma classe que encapsule uma URL e seja Nula ou uma URL válida. É a conversão de uma string em um URL que impõe o contrato e uma exceção é lançada se for inválida. Um método com um parâmetro de URL é muito mais claro que um método com um parâmetro String e uma asserção que especifica um URL.
fonte
Afirmações são para capturar algo que um desenvolvedor fez de errado (não apenas você - outro desenvolvedor de sua equipe também). Se for razoável que um erro do usuário possa criar essa condição, deve ser uma exceção.
Da mesma forma, pense nas consequências. Uma declaração normalmente desliga o aplicativo. Se houver alguma expectativa realista de que a condição possa ser recuperada, você provavelmente deve usar uma exceção.
Por outro lado, se o problema puder ser causado apenas por um erro do programador, use uma declaração, pois você deseja saber sobre isso o mais rápido possível. Uma exceção pode ser detectada e tratada, e você nunca descobriria isso. E sim, você deve desativar as declarações no código de lançamento, porque deseja que o aplicativo se recupere se houver a menor chance possível. Mesmo se o estado do seu programa estiver profundamente quebrado, o usuário poderá salvar seu trabalho.
fonte
Não é exatamente verdade que "a declaração falha apenas no modo de depuração".
Em Object Oriented Software Construction, 2ª Edição, de Bertrand Meyer, o autor deixa uma porta aberta para verificar as pré-condições no modo de liberação. Nesse caso, o que acontece quando uma asserção falha é que ... uma exceção de violação de asserção é criada! Nesse caso, não há recuperação da situação: algo útil pode ser feito e é gerar automaticamente um relatório de erros e, em alguns casos, reiniciar o aplicativo.
A motivação por trás disso é que as pré-condições são geralmente mais baratas de testar do que invariantes e pós-condições, e que, em alguns casos, a correção e a "segurança" na criação da versão são mais importantes que a velocidade. Para muitas aplicações, a velocidade não é um problema, mas a robustez (a capacidade do programa de se comportar de maneira segura quando seu comportamento não é correto, ou seja, quando um contrato é quebrado).
Você deve sempre deixar as verificações de pré-condição ativadas? Depende. Você decide. Não há resposta universal. Se você estiver criando software para um banco, talvez seja melhor interromper a execução com uma mensagem alarmante do que transferir US $ 1.000.000 em vez de US $ 1.000. Mas e se você estiver programando um jogo? Talvez você precise de toda a velocidade possível e, se alguém conseguir 1000 pontos em vez de 10 por causa de um bug que as condições prévias não capturaram (porque não estão ativadas), azar.
Em ambos os casos, idealmente, você deve ter detectado esse bug durante o teste e deve fazer uma parte significativa de seu teste com as afirmações ativadas. O que está sendo discutido aqui é qual é a melhor política para os casos raros nos quais as pré-condições falham no código de produção em um cenário que não foi detectado anteriormente devido a testes incompletos.
Para resumir, você pode ter afirmações e ainda obter as exceções automaticamente , se as deixar ativadas - pelo menos em Eiffel. Eu acho que para fazer o mesmo em C ++, você precisa digitá-lo.
Consulte também: Quando as asserções devem permanecer no código de produção?
fonte
Havia uma enorme discussão sobre a habilitação / desabilitação de asserções nas versões baseadas no comp.lang.c ++. Moderado, que, se você tiver algumas semanas, poderá ver como as opiniões são variadas. :)
Ao contrário do coppro , acredito que, se você não tiver certeza de que uma asserção pode ser desabilitada em uma compilação de versão, ela não deveria ter sido uma afirmação. As asserções são para proteger contra invariáveis do programa serem quebrados. Nesse caso, no que diz respeito ao cliente do seu código, haverá um dos dois resultados possíveis:
Não há diferença para o usuário, no entanto, é possível que as asserções adicionem um custo de desempenho desnecessário no código presente na grande maioria das execuções em que o código não falha.
A resposta para a pergunta realmente depende muito mais de quem serão os clientes da API. Se você estiver escrevendo uma biblioteca que fornece uma API, precisará de alguma forma de mecanismo para notificar seus clientes que eles usaram a API incorretamente. A menos que você forneça duas versões da biblioteca (uma com afirmativas e outra sem), afirmar é muito improvável a escolha apropriada.
Pessoalmente, no entanto, também não tenho certeza se aceitaria exceções neste caso. As exceções são mais adequadas para onde uma forma adequada de recuperação pode ocorrer. Por exemplo, pode ser que você esteja tentando alocar memória. Quando você pega uma exceção 'std :: bad_alloc', pode ser possível liberar memória e tentar novamente.
fonte
Descrevi minha visão sobre o estado da questão aqui: como você valida o estado interno de um objeto? . Geralmente, afirme suas reivindicações e lance por violação de outras pessoas. Para desabilitar declarações em compilações de versão, você pode:
Obviamente, nas compilações de versão, asserções com falha e exceções não capturadas devem ser tratadas de outra maneira que não nas compilações de depuração (onde pode ser chamado apenas de std :: abort). Escreva um log do erro em algum lugar (possivelmente em um arquivo), informe ao cliente que ocorreu um erro interno. O cliente poderá enviar o arquivo de log.
fonte
você está perguntando sobre a diferença entre erros de tempo de design e tempo de execução.
As asserções são 'ei programador, isso está quebrado' notificações, elas estão lá para lembrá-lo de bugs que você não teria notado quando eles aconteceram.
as exceções são: 'ei, usuário, algo deu errado' (obviamente você pode codificar para capturá-las para que o usuário nunca seja informado), mas elas foram projetadas para ocorrer em tempo de execução quando o usuário Joe estiver usando o aplicativo.
Portanto, se você acha que consegue eliminar todos os seus erros, use apenas exceções. Se você acha que não pode ..... usar exceções. Você ainda pode usar declarações de depuração para diminuir o número de exceções, é claro.
Não se esqueça que muitas das condições prévias serão dados fornecidos pelo usuário; portanto, você precisará de uma boa maneira de informar ao usuário que seus dados não foram bons. Para fazer isso, muitas vezes você precisará retornar dados de erro na pilha de chamadas para os bits com os quais ele está interagindo. As declarações não serão úteis então - duplamente, se o seu aplicativo for de n camadas.
Por fim, eu usaria nenhum - os códigos de erro são muito superiores aos erros que você acha que ocorrerão regularmente. :)
fonte
Eu prefiro o segundo. Enquanto seus testes podem ter corrido bem, Murphy diz que algo inesperado vai dar errado. Portanto, em vez de obter uma exceção na chamada de método incorreta real, você acaba rastreando um NullPointerException (ou equivalente) 10 quadros de pilha mais fundo.
fonte
As respostas anteriores estão corretas: use exceções para funções públicas da API. O único momento em que você pode querer desobedecer essa regra é quando o cheque é computacionalmente caro. Nesse caso, você pode colocá-lo em uma afirmação.
Se você acha que a violação dessa condição prévia é provável, mantenha-a como uma exceção ou refatore-a.
fonte
Você deve usar os dois. As declarações são para sua conveniência como desenvolvedor. Exceções capturam coisas que você perdeu ou não esperava durante o tempo de execução.
Apreciei as funções de relatório de erros da glib em vez de afirmações antigas e simples. Eles se comportam como declarações de afirmação, mas, em vez de interromper o programa, retornam um valor e deixam o programa continuar. Funciona surpreendentemente bem e, como bônus, você vê o que acontece com o resto do seu programa quando uma função não retorna "o que deveria". Se travar, você sabe que a verificação de erros é laxista em outro lugar no caminho.
No meu último projeto, usei esse estilo de funções para implementar a verificação de pré-condição e, se uma delas falhar, imprimi um rastreamento de pilha no arquivo de log, mas continuo em execução. Economizei muito tempo de depuração quando outras pessoas encontravam um problema ao executar minha compilação de depuração.
Se eu precisasse de verificação de argumentos em tempo de execução, faria o seguinte:
fonte
Tentei sintetizar várias das outras respostas aqui com meus próprios pontos de vista.
Use asserções para os casos em que você deseja desativá-lo na produção, errando para deixá-los dentro. O único motivo real para desativar na produção, mas não no desenvolvimento, é acelerar o programa. Na maioria dos casos, essa velocidade não será significativa, mas às vezes o código é de tempo crítico ou o teste é computacionalmente caro. Se o código é essencial, as exceções podem ser as melhores, apesar da desaceleração.
Se houver alguma chance real de recuperação, use uma exceção, pois as asserções não foram projetadas para serem recuperadas. Por exemplo, o código raramente é projetado para se recuperar de erros de programação, mas é projetado para se recuperar de fatores como falhas de rede ou arquivos bloqueados. Erros não devem ser tratados como exceções simplesmente por estar fora do controle do programador. Em vez disso, a previsibilidade desses erros, em comparação com os erros de codificação, os torna mais passíveis de recuperação.
Re-argumento de que é mais fácil depurar asserções: O rastreamento de pilha de uma exceção nomeada corretamente é tão fácil de ler quanto uma asserção. Um bom código deve capturar apenas tipos específicos de exceções; portanto, as exceções não devem passar despercebidas devido à captura. No entanto, acho que o Java às vezes o força a capturar todas as exceções.
fonte
A regra geral, para mim, é que use expressões assertivas para encontrar erros internos e exceções para erros externos. Você pode se beneficiar muito da discussão a seguir, de Greg, daqui .
PS: você pode querer verificar a pergunta semelhante: Exceção versus afirmação .
fonte
Veja também esta pergunta :
fonte