Uso de cordas / números mágicos [fechado]

31

Este é um tópico um tanto controverso, e acho que existem tantas opiniões quanto programadores. Mas, para o efeito, quero saber quais são as práticas comuns nos negócios (ou nos seus locais de trabalho).

No meu local de trabalho, temos diretrizes rígidas de codificação. Uma seção é dedicada a seqüências / números mágicos. Ele afirma (para C #):

Não use valores literais, numéricos ou seqüências de caracteres, em seu código, exceto para definir constantes simbólicas. Use o seguinte padrão para definir constantes:

public class Whatever  
{  
   public static readonly Color PapayaWhip = new Color(0xFFEFD5);  
   public const int MaxNumberOfWheels = 18;  
}

Há exceções: os valores 0, 1 e nulo quase sempre podem ser usados ​​com segurança. Muitas vezes, os valores 2 e -1 também são bons. As seqüências destinadas ao log ou rastreio estão isentas desta regra. Literais são permitidos quando seu significado é claro a partir do contexto e não estão sujeitos a mudanças futuras.

mean = (a + b) / 2; // okay  
WaitMilliseconds(waitTimeInSeconds * 1000); // clear enough

Uma situação ideal seria algum trabalho de pesquisa oficial mostrando efeitos sobre a legibilidade / manutenção do código quando:

  • Números / cordas mágicos estão por todo o lado
  • Cordas / números mágicos são substituídos por declarações constantes razoavelmente (ou em diferentes graus de cobertura) - e não grite comigo por usar "razoavelmente", eu sei que todo mundo tem uma idéia diferente do que "razoavelmente" é
  • Cordas / números mágicos são substituídos em excesso e em lugares onde eles não precisariam estar (veja meu exemplo abaixo)

Eu gostaria de fazer isso para ter alguns argumentos científicos ao discutir com um de meus colegas, que está indo ao ponto de declarar constantes como:

private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

Outro exemplo seria (e este está em JavaScript):

var someNumericDisplay = new NumericDisplay("#Div_ID_Here");

Você coloca IDs de DOM em cima do seu arquivo javascript se esse ID for usado apenas em um só lugar?

Li os seguintes tópicos:
StackExchange
StackOverflow
Bytes Comunidade de TI
Há muitos mais artigos e, após a leitura desses padrões, surgem alguns padrões.

Então, minha pergunta é: devemos usar cordas e números mágicos em nosso código? Estou procurando especificamente respostas de especialistas que sejam apoiadas por referências, se possível.

Daniel Gruszczyk
fonte
2
Uma variável mágica é uma variável que possui um significado que não é refletido por seu conteúdo. O valor inteiro '10' reflete o significado do número 10, portanto, não há necessidade de torná-lo uma constante. O mesmo vale para espaço e ponto e vírgula. Por outro lado, se você tiver um valor '%% ?? %%' e esse for um delimitador personalizado, ele deverá ser colocado como uma constante, pois seu conteúdo não reflete o fato de ser um delimitador.
Jeroen Vannevel
23
NumberTen = 10Isso é inútil, pois o número 10 não será redefinido. MaxRetryCount = 10Isso tem um ponto a: podemos querer alterar a contagem máxima de novas tentativas. private const char SemiColon = ';'; Burro. private const char LineTerminator = ';'; Inteligente.
11763 Mike
1
A pergunta real não está clara.
Tulains Córdova

Respostas:

89

... ao discutir com um dos meus colegas, que vai declarar constantes como:

private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

O argumento que você precisa apresentar ao seu colega não é sobre nomear um espaço literal, Spacemas sua má escolha de nome para suas constantes.

Digamos que o trabalho do seu código é analisar um fluxo de registros que contêm campos separados por ponto a;b;ce vírgula ( ) e são separados por espaços ( a;b;c d;e;f). Se quem escreveu suas especificações telefonar para você daqui a um mês e disser "estávamos enganados, os campos nos registros são separados por símbolos de barra vertical ( a|b|c d|e|f)", o que você faz?

No esquema de valor como nome que seu colega prefere, você teria que alterar o valor do literal ( SemiColon = '|') e viver com o código que continua a ser usado SemiColonpara algo que não é mais um ponto e vírgula. Isso levará a comentários negativos nas revisões de código . Para diminuir isso, você pode alterar o nome do literal para PipeSymbole passar e alterar todas as ocorrências de SemiColonpara PipeSymbol. Nesse ritmo, você também pode ter usado um ponto-e-vírgula literal ( ';') em primeiro lugar, porque terá que avaliar cada uso individualmente e estará fazendo o mesmo número de alterações.

Identificadores para constantes precisam ser descritivos do que o valor faz , não o que o valor é , e é aí que o seu colega fez uma curva à esquerda para as ervas daninhas. No aplicativo de divisão de campos descrito acima, o objetivo do ponto-e-vírgula é um separador de campos e as constantes devem ser nomeadas de acordo:

private const char FieldSeparator = ';';    // Will become '|' a month from now
private const char RecordSeparator = ' ';
private const int MaxFieldsPerRecord = 10;

Dessa forma, quando o separador de campos é alterado, você altera exatamente uma linha de código, a declaração da constante. Alguém olhando a alteração verá apenas uma linha e imediatamente entenderá que o separador de campos mudou de um ponto e vírgula para um símbolo de tubo. O restante do código, que não precisou ser alterado porque usava uma constante, permanece o mesmo, e o leitor não precisa cavar para ver o que mais foi feito.

Blrfl
fonte
Eu concordo totalmente. Algumas décadas atrás, trabalhei em um projeto em que as mensagens eram enviadas em segmentos, cada um usando 8 registros gerais. Alguém havia declarado #define one 1 #define two 2 etc (ou qualquer que fosse o equivalente no UK Post Office Coral, o então idioma de escolha). Palavra veio do alto, que, no futuro, o campo de comprimento seria o número de bytes, não segmentos, por isso , obviamente, o código foi alterado para #define one 8 #define two 16 etc
MAWG
3
Por mais bobo que nomes como Ponto e vírgula ou PipeSymbol parecer, mudando um para o outro usando um script será muito mais fácil do que mudar todos os afetados ;a |.
Brandin
E o caso em que um determinado literal String é usado muitas vezes em um arquivo, mas não tem outro significado além do seu valor? Por exemplo, se você está testando que você pode receber uma determinada chave em um mapa em 20 cenários de diferença, devo definir uma constante como assim ?: public static final String MY_KEY_NAME = "MyKeyName"
Jordan McQueen
1
@JordanMcQueen Há um argumento a ser usado para usar literais simples se (e somente se) cada um for usado exatamente uma vez e não for necessário em nenhum outro lugar. Se é algo como cada código ser cenário que processa um formato de arquivo diferente, cada formato deve definir o seu próprio constante (por exemplo, CSV_RECORD_SEPARATOR, TSV_RECORD_SEPARATOR, etc.).
Blrfl
8

Definir ponto e vírgula como uma constante é redundante, porque ponto e vírgula e já é constante por si só . Isso nunca vai mudar.

Não é como se um dia alguém anunciasse "mudança de terminologia, + é o novo ponto e vírgula agora", e seu colega se apressaria feliz em atualizar a constante (eles riram de mim - olhe para eles agora).

Há também uma questão de consistência. Eu garanto que o seuNumberTen constante NÃO será usada por todos (a maioria dos programadores não está maluca), por isso não servirá ao propósito esperado. Quando o apocalipse chegar e "dez" for redimensionado globalmente para 9, atualizar a constante NÃO funcionará, pois ainda lhe deixará um monte de literais 10s no seu código, agora o sistema se torna totalmente imprevisível, mesmo dentro do escopo de uma suposição revolucionária de que "dez" significa "9".

Armazenar todas as configurações como consts é algo que eu também teria dúvidas. Não se deve fazer isso de ânimo leve.

Que exemplos desse tipo de uso reunimos até agora? Terminador de linha ... contagem máxima de novas tentativas ... número máximo de rodas ... temos certeza de que elas nunca mudarão?

O custo é que a alteração das configurações padrão requer a recompilação de um aplicativo e, em alguns casos, até suas dependências (como os valores numéricos de const podem ser codificados durante a compilação).

Há também o aspecto de teste e zombaria. Você definiu a cadeia de conexão como uma const, mas agora opa, não é possível simular o acesso ao banco de dados (estabelecer uma conexão falsa) em seu teste de unidade.

Konrad Morawski
fonte
4
"Isso nunca vai mudar." Eu costumava pensar isso sobre o apóstrofo (sempre ligado ao valor ASCII 39). Alguns aplicativos antigos costumavam enrolar o apóstrofo. Mas agora os aplicativos modernos tratam esse valor ASCII como um apóstrofo direto compatível com aplicativos antigos e, em vez disso, as pessoas costumam usar (aspas simples à esquerda, Unicode 8217 ) para aplicativos compatíveis com a exibição de um glifo diferente para a marca ondulada. Como a Europa usa vírgulas da mesma forma que os americanos usam períodos como ponto decimal, eu me sinto um pouco hesitante em declarar "nunca ... nunca".
TOOGAM
@TOOGAM bem, seu exemplo justifica ter uma DecimalPointconstante - mas não Commaou Periodconstantes. É uma grande diferença: o primeiro denota uma função , um papel ou um propósito do valor. "Ponto e vírgula" ou "vírgula" não se enquadram nessa categoria.
Konrad Morawski
Isso é verdade para o exemplo do ponto decimal. No entanto, o exemplo do apóstrofo parece ser uma categoria semelhante (ou idêntica) a uma vírgula (ou ponto e vírgula).
TOOGAM 12/08/16
O @KonradMorawski Ponto-e-vírgula pode ser usado para muitos propósitos, como dividir a string ou terminar a linha. É o significado dele (não o valor) que deve ser usado para nomear constância. Considere a mudança futura, ou seja, amanhã permitiremos que 20 registros sejam processados, para que a constância nomeada como NumberTen esteja fora de contexto, enquanto maxRecord ainda estaria bom.
MaxZoom 15/12
5
private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

Portanto, seu colega está buscando uma entrada diária no WTF. Essas definições são tolas e redundantes. No entanto, como foi apontado por outros, as seguintes definições não seriam tolas ou redundantes:

private const char StatementTerminator = ';';
private const char Delimiter = ' ';
private const int  BalanceInquiryCode = 10;

Números e cordas "mágicos" são constantes que têm significado além de seu valor imediato e literal. Se a constante 10tiver um significado além de "dez coisas" (digamos, como um código para uma operação específica ou condição de erro), é quando ela se torna "mágica" e deve ser substituída por uma constante simbólica que descreve esse significado abstrato.

Além de descrever claramente a intenção, as constantes simbólicas também poupam algumas dores de cabeça quando você escreve incorretamente um literal. Uma simples transposição de "CVS" para "CSV" em uma linha de código passou por todos os testes de unidade e controle de qualidade e entrou na produção, onde causou falha de uma operação específica. Sim, obviamente, os testes de unidade e controle de qualidade estavam incompletos e esse é o seu próprio problema, mas o uso de uma constante simbólica teria evitado completamente esse azia.

John Bode
fonte
3

Não deveria haver nada controverso sobre isso. O ponto não é se queremos usar números mágicos ou não, o ponto é ter um código legível.
Considere a diferença entre: if(request.StatusCode == 1)e if(request.HasSucceeded). Nesse caso, eu diria que o último é muito mais legível, mas isso não significa que você nunca pode ter um código como esse int MaxNumberOfWheels = 18.

PS: É por isso que eu odeio absolutamente as diretrizes de codificação. Os desenvolvedores devem ser maduros o suficiente para serem capazes de fazer julgamentos assim; eles não devem deixar para um pedaço de texto formado por Deus sabe quem.

Stefan Billiet
fonte
13
Os condutores devem ser maduro o suficiente para ser capaz de fazer o julgamento de qual lado da estrada que dirigem;)
Konrad Morawski
2
O resultado de uma chamada de julgamento pode variar mesmo entre desenvolvedores maduros, portanto, mesmo as diretrizes de codificação arbitrárias visam melhorar a legibilidade através da consistência. Isso não tem relação com o fato de que criar um NumberTen constante não faz sentido.
Mike Partridge
1
Eu não insistiria que eles precisam ser formais, carimbados, etc., podem ser informais, mas devem ser acordados, e isso já vai além do uso da maturidade individual de julgamento. Mas você excluiu seu comentário agora Stefan :)
Konrad Morawski
1
@StefanBilliet - de jeito nenhum. Meu argumento é que a legibilidade é aprimorada através da consistência. O problema aqui não é a diretriz de codificação propriamente dita, mas uma diretriz levada ao extremo por meio de mal-entendidos.
Mike Partridge
@ MikePartridge Talvez eu devesse ter elaborado; as diretrizes de codificação que eu vi são mais na tendência de um livro de regras gerais sobre como alguém em algum lugar pensou que o software deve ser escrito, ao invés de acordos como você e Konrad provavelmente está pensando em :-)
Stefan Billiet