Pergunta

Durante a programação por contrato uma função ou método primeiro verifica se suas pré-condições são cumpridas, antes de começar a trabalhar em suas responsabilidades, certo? As duas formas mais proeminentes para fazer essas verificações são de assert e por exception.

  1. assert não apenas em modo de depuração. Para se certificar de que é crucial para o teste (unidade) todas as pré-condições contratuais separados para ver se eles realmente falhar.
  2. exceção falha no modo de depuração e liberação. Isto tem a vantagem de que o comportamento de depuração testado é idêntico ao liberar comportamento, mas incorre em uma penalidade de desempenho de tempo de execução.

Qual deles você acha que é preferível?

Veja a pergunta releated aqui

Foi útil?

Solução

A desativação assert em compilações é como dizer "eu nunca terá qualquer problema qualquer em uma compilação de lançamento", que muitas vezes não é o caso. Então assert não deve ser desativado em uma compilação de lançamento. Mas você não quer que a compilação de lançamento bater sempre que ocorrerem erros quer, não é?

Portanto, use exceções e usá-los bem. Use um bom, hierarquia exceção sólida e garantir que você pegar e você pode colocar um gancho no arremesso exceção no seu depurador para pegá-lo, e no modo de versão você pode compensar o erro ao invés de um acidente de straight-up. É a maneira mais segura para ir.

Outras dicas

A regra de ouro é que você deve usar afirmações quando você está tentando pegar seus próprios erros e exceções ao tentar capturar erros de outras pessoas. Em outras palavras, você deve usar exceções para verificar os pré-requisitos para as funções API pública, e sempre que você receber quaisquer dados que são externos ao sistema. Você deve usar afirma para as funções ou dados que são internas ao sistema.

A I follow princípio é este: se uma situação pode ser realisticamente evitado pela codificação em seguida, usar uma afirmação. Caso contrário, use uma exceção.

As afirmações são para garantir que o contrato está a ser respeitado. O contrato deve ser justo, para que o cliente deve estar em uma posição para garantir a conformidade. Por exemplo, você pode indicar em um contrato que a URL deve ser válido porque as regras sobre o que é e não é uma URL válida são conhecidos e consistente.

As exceções são para situações que estão fora do controle de ambos o cliente eo servidor. Um meio de exceção que alguma coisa tem de errado se foi, e não há nada que poderia ter sido feito para evitá-lo. Por exemplo, a conectividade de rede está fora dos aplicativos controlar então não há nada que pode ser feito para evitar um erro de rede.

Eu gostaria de acrescentar que a distinção de asserção / Exceção não é realmente a melhor maneira de pensar sobre isso. O que você realmente quer estar pensando é o contrato e como ela pode ser aplicada. No meu exemplo URL acima, que melhor coisa a fazer é ter uma classe que encapsula uma URL e é nulo ou uma URL válida. É a conversão de uma string em uma URL que impõe o contrato, e uma exceção é lançada se for inválido. Um método com um parâmetro de URL é muito mais claro que um método com um parâmetro de cadeia e uma afirmação de que especifica um URL.

Afirma são para pegar algo um desenvolvedor tem feito de errado (e não apenas a si mesmo - outro desenvolvedor em sua equipe também). Se é razoável que um erro usuário pode criar esta condição, então ele deve ser uma exceção.

Da mesma forma pensar sobre as conseqüências. Uma declaração normalmente desliga o aplicativo. Se houver qualquer expectativa realista de que a condição poderia ser recuperada a partir, você provavelmente deve usar uma exceção.

Por outro lado, se o problema pode única ser devido a um erro do programador, em seguida, usar uma declaração, porque você quer saber sobre ele o mais rápido possível. Uma exceção pode ser capturada e tratada, e você nunca iria descobrir mais sobre ele. E sim, você deve desabilitar afirma no código de liberação, porque não quiser que o aplicativo para recuperar se houver a menor chance de que poderia. Mesmo que o estado de seu programa está profundamente quebrado o usuário só poderia ser capaz de salvar o seu trabalho.

Não é exatamente verdade que "assert não apenas em modo de depuração."

Em Object Oriented Software Construction, 2nd Edition por Bertrand Meyer, as folhas autor uma porta aberta para a verificação pré-condições no modo de versão. Nesse caso, o que acontece quando uma afirmação falhar é que ... uma exceção afirmação violação é levantada! Neste caso, não há recuperação da situação: algo útil poderia ser feito, porém, e é para gerar automaticamente um relatório de erro e, em alguns casos, para reiniciar o aplicativo

.

A motivação por trás disso é que pré-condições são normalmente mais barato para teste de invariantes e pós-condições, e que em alguns casos correção e "segurança" na compilação de lançamento são mais importantes do que a velocidade. ie Para a velocidade muitas aplicações não é um problema, mas robustez (a capacidade do programa para se comportar de uma maneira segura quando seu comportamento não é correto, ou seja, quando um contrato é quebrado) é.

Se você sempre deixar cheques pré-condição habilitado? Depende. Você decide. Não existe uma resposta universal. Se você estiver fazendo software para um banco, que poderia ser melhor para a execução de interrupção com uma mensagem alarmante do que para transferir US $ 1.000.000 em vez de US $ 1.000. Mas e se você está programando um jogo? Talvez você precisa de toda a velocidade que você pode começar, e se alguém recebe 1000 pontos em vez de 10 por causa de um bug que as condições prévias não pegar (porque não está habilitado), azar.

Em ambos os casos você deve idealmente ter catched que bug durante o teste, e você deve fazer uma parte significativa do seu teste com afirmações habilitado. O que está sendo discutido aqui é o que é a melhor política para os casos raros em que condições falham no código de produção em um cenário que não foi detectada mais cedo devido ao teste incompleto.

Para resumir, você pode ter afirmações e ainda obter as exceções automaticamente , se você deixá-los habilitado - pelo menos em Eiffel. Eu acho que para fazer o mesmo em C ++ você precisa digitar você mesmo.

Veja também: Quando deve afirmações ficar no código de produção

Houve uma enorme enrosque sobre a activação / desactivação de afirmações em compilações em comp.lang.c ++. moderado, que se você tiver algumas semanas você pode ver como variou as opiniões sobre este são. :)

Ao contrário do coppro , I acredito que se você não tiver certeza de que uma afirmação pode ser desativado em uma compilação de lançamento, então não deve ter sido uma declaração. Afirmações são para proteger contra invariantes do programa que está sendo quebrado. Em tal caso, um, tanto quanto o cliente do seu código está em causa, haverá um de dois resultados possíveis:

  1. Die com algum tipo de falha tipo de sistema operacional, resultando em uma chamada para abortar. (Sem assert)
  2. Die através de uma chamada direta para abortar. (Com assert)

Não há nenhuma diferença para o usuário, no entanto, é possível que as afirmações adicionar um custo de desempenho desnecessária no código que está presente na grande maioria das corridas em que o código não falha.

A resposta para a pergunta realmente depende muito mais que os clientes da API será. Se você estiver escrevendo uma biblioteca fornecendo uma API, então você precisa de alguma forma de mecanismo para notificar seus clientes que eles têm usado a API incorretamente. A menos que você fornecer duas versões da biblioteca (um com afirma, um sem), então assert é muito improvável que a escolha adequada.

Pessoalmente, no entanto, não tenho certeza que eu iria com exceções para este caso também. Exceções são mais adequados para onde uma forma adequada de recuperação pode ter lugar. Por exemplo, pode ser que você está tentando alocar memória. Quando você pegar um 'std :: bad_alloc' exceção pode ser possível para liberar memória e tente novamente.

Eu delineado meu ponto de vista sobre o estado da questão aqui: Como você validar o estado interno de um objeto? . Geralmente, afirmar suas reivindicações e jogar por violação por outros. Para desativação afirma em compilações, você pode fazer:

  • Desativar afirma para verificações caros (como verificar se um intervalo é ordenado)
  • Mantenha verificações triviais habilitado (como a verificação de um ponteiro nulo ou um valor booleano)

Claro que, em compilações, afirmações com falha e exceções.Ele deve ser tratado de outra maneira do que em compilações de depuração (onde ele poderia simplesmente chamar std :: abort). Escrever um log do erro em algum lugar (possivelmente em um arquivo), dizer ao cliente que ocorreu um erro interno. O cliente será capaz de enviar-lhe o arquivo de log.

você está perguntando sobre a diferença entre erros em tempo de execução tempo de design e.

afirma são 'hey programador, este é quebrado' notificações, eles estão lá para lembrá-lo de erros que você não teria percebido quando aconteceu.

exceções são 'hey usuário, Algo está errado Gone' notificações (obviamente você pode codificar para pegá-los para que o usuário nunca se disse), mas estes são projetados para ocorrer em tempo de execução quando o usuário Joe está usando o aplicativo.

Então, se você acha que você pode obter todos os seus erros para fora, use apenas exceções. Se você acha que não pode ..... usar exceções. Você ainda pode usar depuração afirma para fazer o número de exceções menos claro.

Não se esqueça de que muitas das pré-condições serão dados fornecidos pelo usuário, assim você terá uma boa maneira de informar o usuário seus dados não era bom. Para fazer isso, muitas vezes você vai precisar retornar dados de erro para baixo da pilha de chamadas para os bits que ele está interagindo com. Afirma não será útil, em seguida, -. Duplamente por isso, se seu aplicativo é n-tier

Por fim, eu usaria nem - códigos de erro são muito superiores para erros você acha que vai ocorrer regularmente. :)

Eu prefiro a segunda. Enquanto os testes podem ter corrido bem, Murphy diz que algo inesperado vai dar errado. Assim, em vez de obter uma exceção na chamada de método errôneo real, você acaba traçando um NullPointerException (ou equivalente) 10 quadros de pilha mais profunda.

As respostas anteriores estão corretas: usar exceções para funções API pública. A única vez que você pode querer dobrar esta regra é quando o cheque é computacionalmente caro. Nesse caso, você pode colocá-lo em uma declaração.

Se você acha violação dessa condição é provável, mantê-lo como uma exceção, ou refatorar o pré-requisito de distância.

Você deve usar ambos. Afirma são para sua conveniência como um desenvolvedor. Exceções pegar coisas que você perdeu ou não esperar durante tempo de execução.

Eu cresci Apaixonado por funções de relatório de erro do loquazes em vez de planície antiga afirma. Eles se comportam como instruções Assert, mas ao invés de interromper o programa, eles retornam apenas um valor e deixe o programa continuar. Ele funciona surpreendentemente bem, e como um bônus que você começa a ver o que acontece com o resto do seu programa quando uma função não retorna "o que é suposto". Se ele falhar, você sabe que a sua verificação de erros está em algum lugar frouxa outra coisa no caminho.

No meu último projeto, eu usei estes estilo de funções para implementar a verificação de pré-condição, e se um deles falhar, eu iria imprimir um rastreamento de pilha no arquivo de log, mas continuar a correr. Me salvou toneladas de depuração momento em que outras pessoas iriam encontrar um problema ao executar a minha compilação de depuração.

#ifdef DEBUG
#define RETURN_IF_FAIL(expr)      do {                      \
 if (!(expr))                                           \
 {                                                      \
     fprintf(stderr,                                        \
        "file %s: line %d (%s): precondition `%s' failed.", \
        __FILE__,                                           \
        __LINE__,                                           \
        __PRETTY_FUNCTION__,                                \
        #expr);                                             \
     ::print_stack_trace(2);                                \
     return;                                                \
 };               } while(0)
#define RETURN_VAL_IF_FAIL(expr, val)  do {                         \
 if (!(expr))                                                   \
 {                                                              \
    fprintf(stderr,                                             \
        "file %s: line %d (%s): precondition `%s' failed.",     \
        __FILE__,                                               \
        __LINE__,                                               \
        __PRETTY_FUNCTION__,                                    \
        #expr);                                                 \
     ::print_stack_trace(2);                                    \
     return val;                                                \
 };               } while(0)
#else
#define RETURN_IF_FAIL(expr)
#define RETURN_VAL_IF_FAIL(expr, val)
#endif

Se eu precisasse de tempo de execução verificação de argumentos, eu faria isso:

char *doSomething(char *ptr)
{
    RETURN_VAL_IF_FAIL(ptr != NULL, NULL);  // same as assert(ptr != NULL), but returns NULL if it fails.
                                            // Goes away when debug off.

    if( ptr != NULL )
    {
       ...
    }

    return ptr;
}

Eu tentei sintetizar várias das outras respostas aqui com meus próprios pontos de vista.

Use afirmações para casos em que você deseja desativá-lo na produção, que erram para deixá-los em. A única verdadeira razão para desativar na produção, mas não em desenvolvimento, é acelerar o programa. Na maioria dos casos, esta velocidade se não será significativo, mas às vezes o código é momento crítico ou o teste é computacionalmente caro. Se o código é de missão crítica, em seguida, exceções podem ser melhor, apesar da desaceleração.

Se houver qualquer chance real de recuperação, use uma exceção como afirmações não são projetados para ser recuperados a partir. Por exemplo, o código é raramente concebida para se recuperar de erros de programação, mas é projetado para se recuperar de fatores, tais como falhas de rede ou arquivos bloqueados. Os erros não devem ser tratadas como exceções simplesmente por estar fora do controle do programador. Em vez disso, a previsibilidade destes erros, em comparação com erros de codificação, torna-os mais amável para a recuperação.

Re argumento de que é mais fácil de afirmações de depuração: O rastreamento de pilha de uma exceção devidamente nomeado é tão fácil de ler como uma afirmação. Bom código só deve pegar tipos específicos de exceções, de modo exceções não deve ir devido despercebida para ser apanhado. No entanto, penso Java, por vezes, obriga a capturar todas as exceções.

Veja também esta questão :

I alguns casos, afirma são desativados quando a construção para a liberação. Você pode não tem controle sobre isso (caso contrário, você poderia construir com afirma on), por isso pode ser uma boa idéia para fazê-lo assim.

O problema com "corrigir" os valores de entrada é que o chamador não entendo o que eles esperam, e isso pode levar a problemas ou mesmo acidentes em todo diferentes partes do programa, tornando a depuração de um pesadelo.

Eu costumo lançar uma exceção na instrução if para assumir o papel do assert no caso de serem desativados

assert(value>0);
if(value<=0) throw new ArgumentOutOfRangeException("value");
//do stuff

A regra de ouro, para mim, é que o uso assert expressões para encontrar erros internos e exceções para erros externos. Você pode se beneficiar muito com a discussão a seguir por Greg de aqui .

expressões

Assert são usados ??para encontrar erros de programação: ou erros na própria lógica do programa ou em erros na sua implementação correspondente. Um verifica condição afirmam que o programa permanece em um estado definido. A "estado definido" é basicamente um que concorda com os pressupostos do programa. Note-se que um "estado definido" para uma necessidade programa não ser um "estado ideal" ou mesmo "um estado usual", ou até mesmo um "estado útil" mas mais sobre esse ponto importante mais tarde.

Para entender como afirmações caber em um programa, considere uma rotina em um programa de C ++ que está prestes a Dereference um ponteiro. Agora deve ser o teste de rotina se o ponteiro é nulo antes da desreferência, ou deveria afirmar que o ponteiro não é nulo e, em seguida, vá em frente e desreferenciava-lo independentemente?

Eu imagino que a maioria dos desenvolvedores gostaria de fazer as duas coisas, adicione o assert, mas também verificar o ponteiro para um valor NULL, a fim de não falhar deve a condição afirmado falhar. Na superfície, realizando tanto o teste ea verificação pode parecer a decisão mais sábia

Ao contrário de suas condições afirmadas, tratamento de erros de um programa (excepções) não se refere a erros no programa, mas a entradas o programa obtidos junto dos seus meio Ambiente. Estes são muitas vezes "erros" na parte de alguém, como um usuário a tentar entrar em uma conta sem digitar uma senha. E mesmo que o erro pode impedir que uma conclusão bem sucedida do programa tarefa, não há nenhuma falha do programa. O programa não consegue fazer o login do usuário sem uma senha devido a um erro externo - um erro do usuário parte. Se as circunstâncias fossem diferentes, e que o usuário digitou no senha correta e o programa não conseguiu reconhecê-lo; em seguida, embora o resultado ainda seria o mesmo, o fracasso seria agora pertencem a o programa.

A finalidade do tratamento de erros (exceções) é duas vezes. O primeiro é o de comunicar para o usuário (ou algum outro cliente) que um erro na entrada do programa tem foi detectado e que isso significa. O segundo objectivo é restaurar o aplicação após o erro é detectado, a um estado bem definido. Nota que o programa em si não está em erro nesta situação. Concedido, o programa pode estar em um estado não-ideal, ou mesmo um estado em que pode fazer nada de útil, mas não há errorl programação. Pelo contrário, desde que o estado de recuperação de erro é um intercetado pelos do programa design, ele iss que o programa pode manipular.

PS: você pode querer verificar para fora a questão semelhante:. Vs Assertion

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