Eliminando números mágicos: quando é hora de dizer "não"?

36

Estamos todos conscientes de que números mágicos (valores codificados) podem causar estragos em seu programa, especialmente quando é hora de modificar uma seção de código que não tem comentários, mas onde você desenha a linha?

Por exemplo, se você tem uma função que calcula o número de segundos entre dois dias, você substitui

seconds = num_days * 24 * 60 * 60

com

seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE

Em que ponto você decide que é completamente óbvio o que significa o valor codificado e deixa isso de lado?

    
por oosterwal 09.03.2011 / 17:30
fonte

10 respostas

39

Existem duas razões para usar constantes simbólicas em vez de literais numéricos:

  1. Para simplificar a manutenção, se os números mágicos mudarem. Isso não se aplica ao seu exemplo. É extremamente improvável que o número de segundos em uma hora ou o número de horas em um dia mude.

  2. Para melhorar a legibilidade. A expressão "24 * 60 * 60" é bastante óbvia para quase todos. "SECONDS_PER_DAY" também é, mas se você estiver procurando um bug, talvez seja necessário verificar se SECONDS_PER_DAY foi definido corretamente. Há valor em brevidade.

Para números mágicos que aparecem exatamente uma vez e são independentes do resto do programa, decidir se um símbolo é criado para esse número é uma questão de gosto. Se houver alguma dúvida, vá em frente e crie um símbolo.

Não faça isso:

public static final int THREE = 3;
    
por 09.03.2011 / 18:45
fonte
29

Eu manteria a regra de nunca ter números mágicos.

Enquanto

seconds = num_days * 24 * 60 * 60

É perfeitamente legível na maior parte do tempo, depois de ter codificado 10 horas por dia durante três ou quatro semanas no modo crunch

seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE

é muito mais fácil de ler.

FrustratedWithFormsA sugestão do designer é melhor:

seconds = num_days * DAYS_TO_SECOND_FACTOR

ou melhor ainda

seconds = CONVERT_DAYS_TO_SECONDS(num_days)

As coisas deixam de ser óbvias quando você está muito cansado. Código defensivamente .

    
por 09.03.2011 / 17:37
fonte
8

A hora de dizer não é quase sempre. Tempos em que eu acho que é mais fácil usar apenas números codificados em lugares como layout de interface do usuário - criar uma constante para o posicionamento de cada controle no formulário fica muito cubmersone e cansativo e se esse código é geralmente tratado por um designer de interface do usuário não importa muito. ... a menos que a interface do usuário seja exposta dinamicamente, ou use posições relativas em alguma âncora ou seja escrita à mão. Nesse caso, eu diria que é melhor definir algumas constantes significativas para o layout. E se você precisar de um fator de correção aqui ou ali para alinhar / posicionar algo "correto", isso também deve ser definido.

Mas no seu exemplo, acho que substituir 24 * 60 * 60 por DAYS_TO_SECONDS_FACTOR é melhor.

Admito que os valores codificados também são aceitáveis quando o contexto e o uso são completamente claros. Isso, no entanto, é um julgamento ...

Exemplo:

Como @rmx apontou, usar 0 ou 1 para verificar se uma lista está vazia ou talvez nos limites de um loop é um exemplo de um caso em que a finalidade da constante é muito clara.

    
por 09.03.2011 / 17:33
fonte
8

Pare quando você não conseguir definir um significado ou uma finalidade para o número.

seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE

é muito mais fácil de ler do que apenas usar os números. (Embora possa ser mais legível por ter uma única constante SECONDS_PER_DAY , mas é uma questão completamente separada.)

Suponha que um desenvolvedor que esteja olhando o código possa ver o que ele faz. Mas não assuma que eles também sabem o porquê. Se sua constante ajuda a entender o porquê, vá em frente. Se não, não faça.

Se você acabar com muitas constantes, como sugerido por uma resposta, considere usar um arquivo de configuração externa, já que ter dezenas de constantes em um arquivo não melhora exatamente a legibilidade.

    
por 09.03.2011 / 19:42
fonte
7

Eu provavelmente diria "não" a coisas como:

#define HTML_END_TAG "</html>"

E definitivamente diria "não" a:

#define QUADRATIC_DISCRIMINANT_COEF 4
#define QUADRATIC_DENOMINATOR_COEF  2
    
por 10.03.2011 / 04:21
fonte
7

Um dos melhores exemplos que encontrei para promover o uso de constantes para coisas óbvias como HOURS_PER_DAY é:

Estávamos calculando quanto tempo as coisas estavam na fila de trabalho de uma pessoa. Os requisitos foram vagamente definidos e o programador codificou em 24 em vários lugares. Eventualmente, percebemos que não era justo punir os usuários por ficarem sentados em um problema por 24 horas, quando eles realmente só trabalham por 8 horas por dia. Quando a tarefa veio para consertar isso E ver que outros relatórios podem ter o mesmo problema, foi muito difícil procurar / pesquisar através do código por 24, seria muito mais fácil procurar / grep por HOURS_PER_DAY

    
por 10.03.2011 / 17:40
fonte
4

Eu acho que, desde que o número seja completamente constante e não tenha possibilidade de mudar, é perfeitamente aceitável. Portanto, no seu caso, seconds = num_days * 24 * 60 * 60 é bom (supondo, é claro, que você não faça algo bobo como esse tipo de cálculo dentro de um loop) e possivelmente melhor para legibilidade do que seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE .

É quando você faz coisas assim que são ruins:

lineOffset += 24; // 24 lines to a page

Mesmo que você não consiga ajustar mais linhas na página ou mesmo que não tenha nenhuma intenção de alterá-la, use uma variável constante, porque um dia ela voltará para assombrá-lo. Em última análise, o ponto é a legibilidade, não salvando 2 ciclos de cálculo na CPU. Isso não é mais 1978, quando os bytes preciosos foram espremidos por todo o seu valor.

    
por 09.03.2011 / 17:48
fonte
3
seconds = num_days * 24 * 60 * 60

Está perfeitamente bem. Estes não são realmente números mágicos, pois nunca irão mudar.

Qualquer número que possa mudar razoavelmente ou não ter nenhum significado óbvio deve ser colocado em variáveis. O que significa praticamente todos eles.

    
por 09.03.2011 / 19:59
fonte
3

Eu evitaria criar constantes (valores mágicos) para converter um valor de uma unidade para outra. No caso de conversão, prefiro um nome de método de fala. Neste exemplo, seria por exemplo DayToSeconds(num_days) internal o método não precisa de valores mágicos porque o significado de "24" e "60" é claro.

Nesse caso, eu nunca usaria segundos / minutos / horas. Eu só usaria TimeSpan / DateTime.

    
por 21.07.2014 / 17:40
fonte
1

Use o contexto como um parâmetro para decidir

Por exemplo, você tem uma função chamada "calculateSecondsBetween: aDay e: anotherDay", você não precisará fazer muito exame sobre o que esses números fazem, porque o nome da função é bastante representativo.

E outra pergunta é, quais são as possibilidades de calcular de uma maneira diferente? Às vezes, há muitas maneiras de fazer a mesma coisa, então, para orientar os futuros programadores e mostrar a eles o método usado, definir constantes pode ajudar a descobrir isso.

    
por 09.03.2011 / 17:36
fonte