Devo lançar uma exceção no caso de um valor significativo fora do intervalo ou lidar com isso sozinho?

42

Eu escrevi uma estrutura que representa coordenadas de latitude / longitude. Seus valores variam de -180 a 180 para longitudes e 90 a -90 para latitudes.

Se um usuário dessa estrutura me der um valor fora desse intervalo, tenho duas opções:

  1. Lance uma exceção (arg fora do intervalo)
  2. Converta o valor para a restrição

Como uma coordenada de -185 tem significado (pode ser facilmente convertida para +175 porque são coordenadas polares), eu poderia aceitá-la e convertê-la.

É melhor lançar uma exceção para dizer ao usuário que seu código me deu um valor que não deveria ter?

Edit: Também eu sei a diferença entre lat / lng e coordenadas, mas eu queria simplificar isso para uma discussão mais fácil - não era a mais brilhante das ideias

    
por K. Gkinis 18.02.2016 / 12:11
fonte

8 respostas

51

Se o núcleo da sua pergunta for este ...

If some client code passes an argument whose value is invalid for the thing that my data structure is modeling, should I reject the value or convert it to something sensible?

... então minha resposta geral seria "rejeitar", porque isso ajudará a chamar a atenção para possíveis erros no código do cliente que estão realmente fazendo com que o valor inválido apareça no programa e alcance seu construtor. Chamar a atenção para os bugs é geralmente uma propriedade desejada na maioria dos sistemas, pelo menos durante o desenvolvimento (a menos que seja uma propriedade desejada do seu sistema para atrapalhar em caso de erros).

A questão é se você está realmente enfrentando esse caso .

  • Se a estrutura de dados se destina a modelar coordenadas polares em geral, aceite o valor porque os ângulos fora do intervalo de -180 e +180 não são realmente inválidos. Eles são perfeitamente válidos e, por acaso, eles sempre têm um equivalente dentro do intervalo de -180 e +180 (e se você quiser convertê-los para segmentar esse intervalo, sinta-se à vontade - o código do cliente geralmente não precisa se importar) .

  • Se a sua estrutura de dados está modelando explicitamente as coordenadas do Web Mercator (de acordo com a pergunta em sua forma inicial), é melhor seguir as provisões mencionadas na especificação (que eu não sei, então eu ganhei) Não diga nada sobre isso). Se a especificação da coisa que você está modelando diz que alguns valores são inválidos, rejeite-os. Se ele diz que eles podem ser interpretados como algo sensato (e, portanto, são realmente válidos), aceite-os.

O mecanismo que você usa para sinalizar se os valores foram aceitos ou não depende dos recursos do seu idioma, sua filosofia geral e seus requisitos de desempenho. Então, você poderia estar lançando uma exceção (no construtor) ou retornando uma versão anulável da sua estrutura (através de um método estático que chama um construtor privado) ou retornando um booleano e passando sua estrutura para o chamador como um parâmetro out ( novamente por meio de um método estático que chama um construtor privado) e assim por diante.

    
por 18.02.2016 / 12:32
fonte
10

Depende muito. Mas você deve decidir para fazer algo e documentá-lo .

A única coisa definitivamente errada para seu código é esquecer de considerar que a entrada do usuário pode estar fora do intervalo esperado e escrever código que acidentalmente tenha algum comportamento. Porque então algumas pessoas farão uma suposição incorreta sobre como seu código se comporta e isso causará bugs, enquanto outros acabarão dependendo do comportamento que seu código acidentalmente tem (mesmo se esse comportamento for completamente insensato) e então você causará mais bugs quando resolver o problema mais tarde.

Neste caso, posso ver os argumentos de qualquer forma. Se alguém viaja +10 graus de 175 graus, eles devem acabar em -175. Se você sempre normalizar a entrada do usuário e assim tratar 185 como equivalente a -175, então o código do cliente não pode fazer a coisa errada quando adiciona 10 graus; sempre tem o efeito certo. Se você tratar 185 como um erro, você forçará todos os casos em que o código do cliente está adicionando graus relativos para colocar na lógica de normalização (ou pelo menos lembre de chamar seu procedimento de normalização), você realmente causará bugs embora esperamos fácil pegar aqueles que serão rapidamente esmagados). Mas se um número de longitude for inserido pelo usuário, escrito literalmente no programa, ou calculado através de algum procedimento destinado a estar sempre em [-180, 180), então um valor fora desse intervalo provavelmente indicará um erro, então "utilmente "convertê-lo pode esconder problemas.

Meu ideal, neste caso, provavelmente seria definir um tipo que represente o domínio correto. Use um tipo abstrato (não deixe o código do cliente simplesmente acessar os números brutos dentro dele) e forneça uma fábrica de normalização e de validação (assim, o cliente pode fazer a compensação). Mas seja qual for o valor deste tipo, 185 deve ser indistinguível de -175 quando visto através de sua API pública (não importa se eles são convertidos em construção ou se você fornece igualdade, acessadores e outras operações que ignoram a diferença de alguma forma) .

    
por 19.02.2016 / 07:54
fonte
3

Se realmente não importa você escolher uma solução, basta deixar o usuário decidir.

Dado que seu objeto struct é readonly value e criado por um método / construtor, você poderia fornecer duas sobrecargas com base nas opções que o usuário possui:

  • Lance uma exceção (arg fora do intervalo)
  • Converta o valor para a restrição

Além disso, nunca permita que o usuário tenha uma estrutura inválida para passar para seus outros métodos. Faça isso corretamente na criação.

Editar: com base nos comentários, suponho que você esteja usando c #.

    
por 18.02.2016 / 13:22
fonte
0

Você deve lançar uma exceção.

No exemplo que você deu, enviando 185 e convertendo-o para -175, pode ser útil em alguns casos fornecer essa funcionalidade. Mas e se o chamador enviar 1 milhão? Eles realmente pretendem converter isso? Parece mais provável que seja um erro. Portanto, se você precisar lançar uma exceção para 1.000.000, mas não para 185, terá que tomar uma decisão sobre um limite arbitrário para lançar uma exceção. Esse limite vai atrapalhar você em algum momento, pois alguns aplicativos de chamadas estão enviando valores pelo limite.

Melhor lançar a exceção para valores fora do intervalo.

    
por 24.02.2016 / 08:16
fonte
0

Depende se a entrada é diretamente de um usuário através de alguma interface do usuário, ou é do sistema.

Entrada por meio de uma interface do usuário

É uma questão de experiência do usuário como lidar com entradas inválidas. Eu não sei sobre o seu caso específico, mas em geral há algumas opções:

  • Alertar o usuário para o erro e fazer com que o usuário corrija-o antes de continuar (Mais comum)
  • Converta automaticamente para o intervalo válido (se possível), mas avise o usuário sobre a alteração e permita que o usuário verifique antes de continuar.
  • Converta silenciosamente para o intervalo válido e continue.

A escolha depende das expectativas dos usuários e da importância dos dados. Por exemplo, o Google corrige automaticamente a ortografia nas consultas, mas isso é de baixo risco, porque uma alteração inútil não é um problema e é fácil de corrigir (e, mesmo assim, fica claro na página de resultados que a consulta foi alterada). Por outro lado, se você estiver inserindo coordenadas para um míssil nuclear, talvez queira uma validação de entrada mais rígida e nenhuma correção silenciosa de dados inválidos. Portanto, não há resposta universal.

Mais importante, você deve considerar se a correção da entrada ainda tem um benefício para o usuário. Por que um usuário inseria dados inválidos? É fácil ver como alguém pode cometer um erro de ortografia, mas por que alguém digitaria uma longitude de -185? Se o usuário realmente quis dizer +175, provavelmente teria digitado +175. Eu acho que é mais provável que uma longitude inválida é simplesmente um erro de digitação, e o usuário quis dizer -85 ou algo mais. Nesse caso, a conversão silenciosa é ruim e inútil . A abordagem mais amigável para o seu aplicativo provavelmente seria alertar o usuário sobre o valor inválido e fazer com que o usuário o corrigisse por conta própria.

Entrada por meio de uma API

Se a entrada for de outro sistema ou subsistema, não há dúvida. Você deveria lançar uma exceção. Você nunca deve silenciosamente converter entrada inválida de outro sistema, uma vez que ela pode mascarar erros em outras partes do sistema. Se a entrada for "corrigida", isso deve acontecer na camada da interface do usuário, e não mais fundo no sistema.

    
por 23.02.2016 / 08:31
fonte
-1

A opção mais conveniente para um desenvolvedor seria um suporte a erro de tempo de compilação na plataforma, para valores fora do intervalo. Nesse caso, o intervalo também deve fazer parte da assinatura do método, assim como o tipo dos parâmetros. Da mesma forma que o usuário da API não pode transmitir uma Seqüência se a assinatura do método estiver definida para ter um inteiro , o usuário não deve ter passado um valor sem verificar se o valor está dentro do intervalo fornecido na assinatura do método. Se não estiver marcado, ele deve receber um erro de tempo de compilação e, assim, o erro de tempo de execução pode ser evitado.

Mas atualmente, muito poucos compiladores / plataformas suportam esse tipo de verificação de tempo de compilação. Então, é uma dor de cabeça dos desenvolvedores. Mas, idealmente, seu método deve apenas lançar uma exceção significativa para valores não suportados e documentá-lo claramente.

BTW, eu realmente amo o modelo de erro proposto por Joe Duffy aqui .

    
por 19.02.2016 / 06:35
fonte
-1

O padrão deve ser lançar uma exceção. Você também pode permitir uma opção como strict=false e fazer a coerção com base no sinalizador, onde é claro que strict=true é o padrão. Isso é bastante comum:

por 19.02.2016 / 01:19
fonte
-2

Para mim, a melhor prática é nunca alterar a entrada do usuário. A abordagem que geralmente faço é separar a validação da execução.

  • Tenha uma classe simples que use apenas os parâmetros fornecidos.
  • Use um decorador para fornecer uma camada de validação que pode ser alterada à vontade sem afetar a classe de execução (ou então injetar um validador se essa abordagem for muito difícil).
por 18.02.2016 / 18:28
fonte