É sempre uma boa idéia codificar valores em nossos aplicativos? Ou é sempre a coisa certa chamar esses tipos de valores dinamicamente, caso eles precisem mudar?
pi
mudança de poder ...
É sempre uma boa idéia codificar valores em nossos aplicativos? Ou é sempre a coisa certa chamar esses tipos de valores dinamicamente, caso eles precisem mudar?
pi
mudança de poder ...
Respostas:
Sim, mas deixe óbvio .
Faz:
Não faça:
diameter = 2 * radius
ou diameter = RADIUS_TO_DIAMETER_FACTOR * radius
? De fato, existem casos em que um número mágico pode ser uma solução melhor.
diameter = radius << 1
? Suponho que também poderia ser diameter = radius << RADIUS_TO_DIAMETER_BITS_TO_SHIFT
.
diameter = radius.toDiameter()
O que acho estranho nessas perguntas e respostas até agora é que ninguém tentou definir claramente "código fixo" ou, mais importante, as alternativas.
tl; dr: Sim, às vezes é uma boa idéia codificar valores, mas não há uma regra simples sobre quando ; depende completamente do contexto.
A pergunta restringe-se a valores , que eu entendo como números mágicos , mas a resposta para saber se eles são ou não uma boa ideia é relativa ao que eles realmente são usados!
Vários exemplos de valores "codificados" são:
Valores de configuração
Eu me arrepio sempre que vejo declarações como essa command.Timeout = 600
. Por que 600? Quem decidiu isso? Foi o tempo limite antes e alguém aumentou o tempo limite como um hack, em vez de corrigir o problema de desempenho subjacente? Ou é realmente alguma expectativa conhecida e documentada para o tempo de processamento?
Estes não devem ser números mágicos ou constantes, devem ser externalizados em um arquivo de configuração ou banco de dados em algum lugar com um nome significativo, porque seu valor ideal é determinado em grande parte ou inteiramente pelo ambiente em que o aplicativo está sendo executado.
Fórmulas matemáticas
As fórmulas geralmente tendem a ser bem estáticas, de modo que a natureza dos valores constantes no interior não é realmente particularmente importante. O volume de uma pirâmide é (1/3) b * h. Nos importamos com a origem do 1 ou 3? Na verdade não. Um comentarista anterior apontou, com razão, que diameter = radius * 2
provavelmente é melhor do que diameter = radius * RADIUS_TO_DIAMETER_CONVERSION_FACTOR
- mas isso é uma dicotomia falsa.
O que você deve fazer para esse tipo de cenário é criar uma função . Não preciso saber como você criou a fórmula, mas ainda preciso saber para que serve . Se, em vez de qualquer bobagem escrita acima, eu escrever volume = GetVolumeOfPyramid(base, height)
, de repente tudo se torna muito mais claro, e é perfeitamente bom ter números mágicos dentro da função ( return base * height / 3
) porque é óbvio que eles são apenas parte da fórmula.
A chave aqui é, obviamente, ter funções curtas e simples . Isso não funciona para funções com 10 argumentos e 30 linhas de cálculos. Use composição ou constantes de funções nesse caso.
Regras de domínio / negócios
Esta é sempre a área cinzenta porque depende de qual é exatamente o valor. Na maioria das vezes, são esses números mágicos específicos que são candidatos à transformação em constantes, porque isso facilita a compreensão do programa sem complicar a lógica do programa. Considere o teste if Age < 19
vs if Age < LegalDrinkingAge
; você provavelmente pode descobrir o que está acontecendo sem a constante, mas é mais fácil com o título descritivo.
Eles também podem se tornar candidatos à abstração de funções, por exemplo function isLegalDrinkingAge(age) { return age >= 19 }
. A única coisa é que geralmente a lógica de negócios é muito mais complicada do que isso, e pode não fazer sentido começar a escrever dezenas de funções com 20 a 30 parâmetros cada. Se não houver uma abstração clara com base em objetos e / ou funções, recorrer a constantes é aceitável.
A ressalva é que, se você trabalha para o departamento tributário, torna-se muito, muito oneroso e honestamente inútil escrever AttachForm(FORM_CODE_FOR_SINGLE_TAXPAYER_FILING_JOINTLY_FOR_DEPRECIATION_ON_ARMPIT_HAIR)
. Você não vai fazer isso, AttachForm("B-46")
porque todo desenvolvedor que já trabalhou ou que irá trabalhar lá saberá que "B-46" é o código do formulário para um único contribuinte que arquiva blá blá blá - os códigos de formulário fazem parte do próprio domínio, nunca mudam; portanto, não são realmente números mágicos.
Então você precisa usar constantes com moderação na lógica de negócios; basicamente, você precisa entender se esse "número mágico" é realmente um número mágico ou se é um aspecto bem conhecido do domínio. Se for domínio, você não o codifica, a menos que haja uma chance muito boa de que isso mude.
Códigos de erro e sinalizadores de status
Isso nunca é bom para codificar, como qualquer pobre bastardo que já foi atingido com o Previous action failed due to error code 46
pode dizer. Se o seu idioma suportar, você deve usar um tipo de enumeração. Caso contrário, você normalmente terá um arquivo / módulo inteiro cheio de constantes especificando os valores válidos para um tipo de erro específico.
Nunca me deixe ver return 42
em um manipulador de erros, capiche? Sem desculpas.
Provavelmente deixei de fora vários cenários, mas acho que isso cobre a maioria deles.
Então, sim, às vezes é uma prática aceitável codificar coisas. Só não seja preguiçoso; deve ser uma decisão consciente, e não um código antigo e desleixado.
Existem vários motivos para atribuir um identificador a um número.
Isso nos fornece critérios para literais codificados. Eles devem ser imutáveis, não difíceis de digitar, ocorrendo apenas em um local ou contexto e com significado reconhecível. Não faz sentido definir 0 como ARRAY_BEGINNING, por exemplo, ou 1 como ARRAY_INCREMENT.
Como um complemento para outras respostas. Use constantes para seqüências de caracteres quando possível. Claro, você não quer ter
const string server_var="server_var";
mas você deveria ter
const string MySelectQuery="select * from mytable;";
(supondo que você realmente tenha uma consulta na qual deseja obter todos os resultados de uma tabela específica, sempre)
Fora isso, use constantes para qualquer número diferente de 0 (geralmente). Se você precisar de uma máscara de permissão de 255, não use
const int 8th_bit=255; //or some other obscure naming scheme that equates to 255.
em vez disso, use
const int AllowGlobalRead=255;
Obviamente, junto com as constantes, sabem quando usar enumeradores. O caso acima provavelmente se encaixaria bem em um.
typedef enum {init_state=0, parse_state=1, evaluation_state=2, ... }
Depende do que você considera codificado. Se você tentar evitar toda e qualquer coisa codificada, você acaba no território da codificação eletrônica e cria um sistema que apenas o criador pode gerenciar (e esse é o código definitivo)
Muitas coisas são codificadas em qualquer estrutura razoável e funcionam. ou seja, não há nenhuma razão técnica para não poder alterar o ponto de entrada de um aplicativo C # (static void Main), mas codificar permanentemente que não cria problemas para nenhum usuário (exceto a pergunta SO ocasional )
A regra de ouro que uso é que tudo o que pode e vai mudar, sem afetar o estado de todo o sistema, deve ser desconfigurável.
Então, IMHO, é bobagem não codificar coisas que nunca mudam (pi, constante gravitacional, uma constante em uma fórmula matemática - pense no volume de uma esfera).
Também é tolice não codificar coisas ou processos que terão um impacto em seu sistema que exigirão programação em qualquer instância, ou seja, é desperdício permitir ao usuário adicionar campos dinâmicos a um formulário, se algum campo adicionado exigir que o desenvolvedor de manutenção entre e escreva um script que faça essa coisa funcionar. Além disso, é estúpido (e já vi isso algumas vezes em ambientes corporativos) criar alguma ferramenta de configuração, para que nada seja codificado, mas apenas os desenvolvedores do departamento de TI podem usá-lo, e é um pouco mais fácil de usá-lo do que para fazer isso no Visual Studio.
Portanto, a questão de saber se uma coisa deve ser codificada é uma função de duas variáveis:
É sempre uma boa idéia codificar valores em nossos aplicativos?
Eu codifico os valores apenas se os valores estiverem especificados na especificação (em uma versão final da especificação), por exemplo, a resposta HTTP OK será sempre 200
(a menos que seja alterada na RFC); assim, você verá (em alguns dos meus códigos ) constantes como:
public static final int HTTP_OK = 200;
Caso contrário, armazeno constantes no arquivo de propriedades.
A razão pela qual especifiquei as especificações é que a alteração de constantes nas especificações requer gerenciamento de alterações, no qual os interessados revisam a alteração e aprovam / desaprovam. Isso nunca acontece da noite para o dia e leva meses / anos para uma aprovação. Não esqueça que muitos desenvolvedores usam especificações (por exemplo, HTTP), portanto, mudar isso significa quebrar milhões de sistemas.
Percebi que sempre que você pode extrair dados do seu código, isso melhora o que resta. Você começa a perceber novas refatorações e aprimora seções inteiras do seu código.
É apenas uma boa idéia trabalhar para extrair constantes, não a considere uma regra estúpida, pense nela como uma oportunidade de codificar melhor.
A maior vantagem seria a maneira como você pode encontrar constantes semelhantes, sendo a única diferença em grupos de código - abstraindo-as em matrizes me ajudou a reduzir alguns arquivos em 90% do seu tamanho e a corrigir alguns erros de copiar e colar enquanto isso. .
Ainda não vi uma única vantagem em não extrair dados.
Eu recentemente codifiquei uma função MySQL para calcular corretamente a distância entre dois pares de lat / long. Você não pode simplesmente fazer pitágoras; as linhas de longitude se aproximam à medida que a latitude aumenta em direção aos polos, então há alguns gatinhos meio peludos envolvidos. A questão é que fiquei bastante impressionado com a possibilidade de codificar o valor que representa o raio da Terra em quilômetros.
Acabei fazendo isso, mesmo que o fato seja que as linhas de latitude / longitude estejam muito mais próximas, digamos, da lua. E minha função subnotificaria drasticamente as distâncias entre pontos em Júpiter. Imaginei que as chances do site que estou construindo com uma localização extraterrestre ser muito pequena.
Bem, depende se o seu idioma é compilado. Se não for compilado, não é grande coisa, basta editar o código-fonte, mesmo que seja um pouco delicado para um não programador.
Se você está programando com uma linguagem compilada, isso claramente não é uma boa ideia, porque se as variáveis mudarem, é necessário recompilar, o que é uma grande perda de tempo, se você deseja ajustar essa variável.
Você não precisa criar um controle deslizante ou interface para alterar dinamicamente a variável dele, mas o mínimo que você pode fazer é um arquivo de texto.
Por exemplo, no meu projeto ogre, estou sempre usando a classe ConfigFile para carregar uma variável que escrevi em um arquivo de configuração.
Duas ocasiões em que as constantes são (na minha opinião pelo menos) OK:
Constantes que não se relacionam com mais nada; você pode alterar essas constantes sempre que quiser, sem precisar alterar mais nada. Exemplo: a largura padrão de uma coluna da grade.
Constantes absolutamente imutáveis, precisas e óbvias, como "número de dias por semana". days = weeks * 7
Substituir 7
por uma constante DAYS_PER_WEEK
dificilmente fornece qualquer valor.
Eu concordo completamente com Jonathan, mas como todas as regras, há exceções ...
"Número mágico na especificação: número mágico no código"
Basicamente, afirma que qualquer número mágico que permaneça na especificação após tentativas razoáveis de obter um contexto descritivo para eles deve ser refletido como tal no código. Se os números mágicos permanecerem no código, todos os esforços devem ser feitos para isolá-los e torná-los claramente vinculados ao seu ponto de origem.
Realizei alguns contratos de interface nos quais é necessário preencher mensagens com valores mapeados no banco de dados. Na maioria dos casos, o mapeamento é bastante direto e caberia nas linhas gerais de Jonathan, mas encontrei casos em que a estrutura da mensagem de destino era simplesmente horrível. Mais de 80% dos valores que precisavam ser transmitidos na estrutura eram constantes impostas pela especificação do sistema distante. isso associado ao fato de que a estrutura da mensagem era gigantesca fazia com que muitas dessas constantes tivessem que ser preenchidas. Na maioria dos casos, eles não forneceram significado ou razão, apenas disseram "coloque M aqui" ou "coloque 4.10.53.10100.889450.4452 aqui". Não tentei colocar um comentário ao lado de todos eles, pois isso tornaria o código resultante ilegível.
Dito isto, quando você pensa sobre isso ... é praticamente tudo sobre tornar isso óbvio ...
Se você está codificando o valor da constante gravitacional da Terra, ninguém vai se importar. Se você codificar o endereço IP do seu servidor proxy, terá problemas.
Principalmente não, mas acho que vale a pena notar que você terá mais problemas ao começar a duplicar o valor codificado. Se você não duplicá-lo (por exemplo, use-o apenas uma vez na implementação de uma classe), não usar uma constante pode ser bom.