Todos os números mágicos são criados da mesma maneira?

77

Em um projeto recente, precisei converter de bytes para kilobytes kibibyte . O código foi bastante simples:

var kBval = byteVal / 1024;

Depois de escrever isso, eu tenho o resto da função trabalhando & seguiu em frente.

Mais tarde, comecei a me perguntar se havia acabado de inserir um número mágico no meu código . Parte de mim diz que foi bom porque o número é uma constante fixa e deve ser prontamente entendido. Mas outra parte de mim acha que teria sido super claro se envolvido em uma constante definida como BYTES_PER_KBYTE .

Então, números que são constantes conhecidas são realmente mágicas ou não?

Perguntas relacionadas:

Quando um número é um número mágico? e Cada número no código é considerado uma "mágica número "? - são semelhantes, mas são questões muito mais amplas do que as que estou perguntando. Minha pergunta é focada em números constantes bem conhecidos que não são abordados nessas questões.

Eliminando números mágicos: quando é O tempo para dizer "Não"? também está relacionado, mas está focado na refatoração, em vez de se um número constante é ou não um número mágico.

    
por GlenH7 17.12.2014 / 20:14
fonte

6 respostas

103

Nem todos os números mágicos são iguais.

Eu acho que nesse caso, essa constante é boa. O problema com os números mágicos é quando eles são mágicos, ou seja, não está claro qual é a sua origem, porque o valor é o que é, ou se o valor está correto ou não.

Ocultar 1024 atrás de BYTES_PER_KBYTE também significa que você não vê instantaneamente se está correto ou não.

Eu esperaria que qualquer um soubesse imediatamente porque o valor é 1024. Por outro lado, se você estivesse convertendo bytes em megabytes, eu definiria a constante BYTES_PER_MBYTE ou similar porque a constante 1.048.576 não é tão óbvia que a sua 1024 ^ 2, ou que é mesmo correto.

O mesmo vale para valores que são ditados por requisitos ou padrões, que são usados apenas em um lugar. Acho que colocar o direito constante no lugar com um comentário para a fonte relevante é mais fácil de lidar do que defini-lo em outro lugar e ter que perseguir ambas as partes, por exemplo:

// Value must be less than 3.5 volts according to spec blah.
SomeTest = DataSample < 3.50

Acho melhor que

SomeTest = DataSample < SOME_THRESHOLD_VALUE

Somente quando SOME_THRESHOLD_VALUE é usado em vários lugares é que a compensação vale a pena definir uma constante, na minha opinião.

    
por 17.12.2014 / 20:29
fonte
44

Há duas perguntas que faço quando se trata de números mágicos.

O número tem um nome?

Os nomes são úteis porque podemos ler o nome e entender o propósito do número por trás dele. Nomear constantes pode aumentar a legibilidade se o nome for mais fácil de entender do que o número que ele substitui e o nome da constante é conciso.

Claramente, constantes como pi, e, et al. tem nomes significativos. Um valor como 1024 pode ser BYTES_PER_KB , mas também espero que qualquer desenvolvedor saiba o que significa 1024. O público-alvo do código-fonte é programadores profissionais que devem ter o conhecimento de vários poderes de dois e por que eles são usados.

É usado em vários locais?

Enquanto nomes são uma força de constantes, outra é reusabilidade. Se é provável que um valor mude, ele pode ser alterado em um lugar, em vez de precisar procurá-lo em vários locais.

Sua pergunta

No caso da sua pergunta, eu usaria o número como está.

Nome: existe um nome para esse número, mas nada é realmente útil. Não representa uma constante matemática ou valor especificado em qualquer documento de requisitos.

Locais: mesmo se usados em vários locais, nunca serão alterados, negando esse benefício.

    
por 17.12.2014 / 20:43
fonte
27

Esta citação

It's not the number that's the problem, it's the magic.

como foi dito por Jörg W Mittag responde muito bem a essa pergunta.

Alguns números simplesmente não são mágicos dentro de um contexto particular. No exemplo fornecido na pergunta, as unidades de medida foram especificadas pelos nomes das variáveis e a operação que estava ocorrendo foi bastante clara.

Portanto, 1024 não é mágico porque o contexto deixa claro que é o valor constante e apropriado a ser usado nas conversões.

Da mesma forma, um exemplo de:

var numDays = numHours / 24; 

é igualmente claro e não é mágico porque é bem conhecido que existem 24 horas no dia.

    
por 17.12.2014 / 20:51
fonte
16

Outros pôsteres mencionaram que a conversão está sendo "óbvia", mas eu discordo. A questão original, neste momento, inclui:

kilobytes kibibytes

Então eu já sei que o autor está ou ficou confuso. A página da Wikipedia aumenta a confusão:

1000 = KB kilobyte (metric)
1024 = kB kilobyte (JEDEC)
1024 = KiB kibibyte (IEC)

Então, "Kilobyte" pode ser usado para significar tanto um fator de 1000 quanto de 1024, com a única diferença na abreviação da capitalização do 'k'. Além disso, 1024 podem significar kilobyte (JEDEC) ou kibibyte (IEC). Por que não despedaçar toda essa confusão com uma constante com um nome significativo? BTW, esse segmento usou "BYTES_PER_KBYTE" com frequência, e isso não é menos ambíguo. KBYTE: é KIBIBYTE ou KILOBYTE? Eu preferiria ignorar o JEDEC e ter BYTES_PER_KILOBYTE = 1000 e BYTES_PER_KIBIBYTE = 1024 . Não há mais confusão.

A razão pela qual pessoas como eu, e muitas outras por aí, têm opiniões 'militantes' (para citar um comentarista aqui) sobre como nomear números mágicos é sobre documentar o que você pretende fazer, e removendo a ambigüidade. E você realmente escolheu uma unidade que levou a muita confusão.

Se eu vejo:

int BYTES_PER_KIBIBYTE = 1024;  
...  
var kibibytes = bytes / BYTES_PER_KIBIBYTE;  

Então fica imediatamente óbvio o que o autor pretendia fazer, e não há ambigüidade. Posso verificar a constante em questão de segundos (mesmo que esteja em outro arquivo), por isso, mesmo que não seja "instantânea", ela está perto o suficiente para ser instantânea.

No final, pode ser óbvio quando você está escrevendo, mas será menos óbvio quando você voltar a ele mais tarde, e pode ser ainda menos óbvio quando alguém o edita. Demora 10 segundos para fazer uma constante; Pode levar meia hora ou mais para depurar um problema com unidades (o código não vai saltar para você e dizer que as unidades estão erradas, você terá que fazer as contas sozinho para descobrir isso, e você provavelmente caçará 10 avenidas diferentes antes de verificar as unidades).

    
por 18.12.2014 / 23:34
fonte
11

A definição de um nome como referência a um valor numérico sugere que sempre que um valor diferente for necessário em um lugar que use esse nome, provavelmente será necessário em todos. Também tende a sugerir que alterar o valor numérico atribuído ao nome é uma maneira legítima de alterar o valor. Tal implicação pode ser útil quando é verdadeira e perigosa quando é falsa.

O fato de dois locais diferentes usarem um valor literal específico (por exemplo, 1024) sugerirá fracamente que as mudanças que levariam um programador a mudar um provavelmente inspirariam o programador a querer mudar os outros, mas essa implicação é muito mais fraca. do que se aplicaria se o programador atribuísse um nome a tal constante.

Um grande perigo com algo como #define BYTES_PER_KBYTE 1024 é que pode sugerir a alguém que encontra printf("File size is %1.1fkB",size*(1.0/BYTES_PER_KBYTE)); que uma maneira segura de fazer o código usar milhares de bytes seria alterar a instrução #define . Tal alteração pode ser desastrosa, no entanto, se, e. algum outro código não relacionado recebe o tamanho de um objeto em Kbytes e usa essa constante ao alocar um buffer para ele.

Pode ser razoável usar #define BYTES_PER_KBYTE_FOR_USAGE_REPORT 1024 e #define BYTES_PER_KBYTE_REPORTED_BY_FNOBULATOR 1024 , atribuindo um nome diferente para cada propósito diferente servido pela constante 1024, mas isso resultaria em muitos identificadores sendo definidos e usados exatamente uma vez. Além disso, em muitos casos, é mais fácil entender o que um valor significa se você vê o código onde ele é usado, e é mais fácil descobrir onde o código significa se você vir os valores de quaisquer constantes usadas nele. Se um literal numérico é usado apenas uma vez para uma finalidade específica, escrever o literal no local em que ele é usado geralmente renderá um código mais compreensível do que atribuir um rótulo a ele em um lugar e usar seu valor em outro lugar.

    
por 17.12.2014 / 22:05
fonte
7

Eu gostaria de usar apenas o número, mas acho que uma questão importante não foi levantada: o mesmo número pode significar coisas diferentes em contextos diferentes, e isso pode complicar a refatoração.

1024 é também o número de KiB por MiB. Suponha que usamos 1024 para representar esse cálculo em algum lugar, ou em vários lugares, e agora precisamos mudar para ele para calcular GiB. Mudar a constante é mais fácil do que um achado / substituição global, onde você pode acidentalmente trocar o errado em alguns lugares, ou perder em outros.

Ou pode ser uma máscara introduzida por um programador preguiçoso que precisa ser atualizado um dia.

É um exemplo inventado, mas em algumas bases de código isso pode causar problemas ao refatorar ou atualizar para novos requisitos. Para este caso em particular, eu não consideraria o número simples muito ruim, especialmente se você puder incluir o cálculo em um método para reutilização, eu provavelmente faria isso sozinho, mas consideraria a constante mais 'correta'.

Se você usar constantes nomeadas, como o supercat diz, é importante considerar se o contexto também é importante e se você precisa de vários nomes.

    
por 18.12.2014 / 01:46
fonte