Magic Numbers, String Literals e Concatenação Composta

Magic Numbers, String Literals e Concatenação CompostaOlá, leitores, tudo certo?
Já ouviram falar em “mágica” na programação? Embora seja um termo que nos lembre algo positivo, na realidade, é um equívoco que desenvolvedores cometem no código, principalmente quando o projeto é colaborativo. A ideia desse artigo é detalhar um pouco mais sobre esse conceito e, claro, apresentar sugestões para evitá-los!

 

Leitores, as dicas presentes neste artigo são básicas, mas contribuem para um código com mais qualidade e profissionalismo, afinal, é isso que almejamos em nossos projetos.
Magic Numbers e String Literals são, respectivamente, números e textos escritos diretamente no código, dificultando duas atividades: interpretação e manutenção. Considere o código abaixo:

if DataSet.FieldByName('Embalagem').AsInteger = 5 then
begin
  result := CalcularFrete(3);
end;

É difícil compreender o que este método retorna. O que significa o número 5? E o número 3 no cálculo do frete? É uma porcentagem? É um tipo de frete? É um valor fixo? Observe que, apesar de simples, o código se torna desnecessariamente complexo. Esse é o problema da interpretação.
A complexidade aumenta um pouco mais quando este mesmo número é utilizado em diferentes partes do sistema:

if nEmbalagem = 5 then
begin
  ValidarPesoCarga;
end;
 
...
 
if PesoMaiorQue10Kilos then
begin
  DataSet.Edit;
  DataSet.FieldByName('Embalagem').AsInteger := 5;
  DataSet.Post;
end;

Essa repetição significa que, caso o tipo da embalagem seja alterado de 5 para 6, todos esses locais do código terão de ser modificados. O que aconteceria se o desenvolvedor esquecesse de alterar um desses locais? Pare um pouco e reflita! 🙂

 

Magic Numbers podem ser evitados ao serem substituídos por constantes. Já comentei sobre isso no blog e estou reforçando!
Pois bem, sabe o que significa o número 5? Embalagem em madeira, meu amigo! Como adivinharíamos?
Sendo assim, podemos melhorar o código criando duas constantes, transmitindo claramente a finalidade destes números:

const
  EMBALAGEM_MADEIRA = 5;
  PORCENTAGEM_EMBALAGEM_MADEIRA = 3;

Agora, veja a diferença no código:

if DataSet.FieldByName('Embalagem').AsInteger = EMBALAGEM_MADEIRA then
begin
  result := CalcularFrete(PORCENTAGEM_EMBALAGEM_MADEIRA);
end;
 
...
 
if nTipo = EMBALAGEM_MADEIRA then
begin
  ValidarPesoCarga;
end;
 
...
 
if PesoMaiorQue10Kilos then
begin
  DataSet.Edit;
  DataSet.FieldByName('Embalagem').AsInteger := EMBALAGEM_MADEIRA;
  DataSet.Post;
end;

É outro nível de codificação, concorda? 😉

 

Quando estudei sobre Magic Numbers, encontrei um desenvolvedor contestando sobre o uso de constantes no Stack Overflow:

“Eu diria que o código fica menos legível constantes. Por que eu criaria uma constante para um número que não irá mudar? Isso apenas deixa o código mais poluído.”

COMO ASSIM?!
Pessoal, isso não é nem questão de opinião. É profissionalismo. Eu, particularmente, já perdi muito tempo buscando o significado de Magic Numbers “enterrados” no código, muitas vezes desconhecidos até pelos Analistas de Sistemas. Se constantes fossem utilizadas, estaríamos livres pelo menos desse horror.

 

O mesmo acontece com as String Literals. Ao utilizar letras em comparações ou atribuições, por exemplo, nos deparamos com as mesmas dificuldades dos números:

if ConsultarSituacaoCliente = 'A' then
begin
  ValidarLimiteDiario;
end;

Você acha que “A” significa “Ativo”? Não, é “Em Atraso”, meu caro! Imagine se utilizássemos o “A” para isentar o cliente de algumas taxas, julgando que ele estivesse ativo? Prefiro nem pensar…
Novamente, poderíamos criar constantes como solução:

const
  EM_ATRASO = 'A';
 
...
 
if ConsultarSituacaoCliente = EM_ATRASO then
begin
  ValidarLimiteDiario;
end;

Ah, antes que eu me esqueça, adquira também o hábito de converter o valor de origem para maiúsculo antes de compará-los, evitando um retorno falso em função do Case Sensitivity:

const
  IMPRESSORA_FISCAL = 'FISCAL';
 
...
 
var
  sTipoImpressora: string;
begin
  sTipoImpressora := UpperCase(ConsultarTipoPapel);
  if sTipoImpressora = IMPRESSORA_FISCAL then
  begin
      AjustarColunasCupom;
  end;
end;

Por hoje é isso, pessoal!

 

Por que o título do artigo menciona Concatenação Composta?
Ops, verdade, esqueci da última parte!

Concatenação composta é a utilização de vários “+” consecutivos para concatenar um texto, como, por exemplo, uma instrução SQL. Porém, esse excesso de concatenações também pode dificultar a interpretação da linha de código. Quer ver?

Query.SQL.Text := 'Insert into CLIENTES (Codigo, Nome, CPF, DataNasc, Cidade) values (' + StrToInt(EditCodigo.Text) + ',' + QuotedStr(EditNome.Text) + ',' + QuotedStr(EditCPF.Text) + ',' + StrToDate(EditDataNasc.Text) + ',' + QuotedStr(EditCidade.Text) + ')';

A leitura torna-se desconfortável, não é? Além disso, no editor de códigos, será necessário mover a barra de rolagem horizontal até o fim da linha para analisar a instrução por completo. É por isso que algumas IDEs de desenvolvimento exibem uma margem do lado direito do editor de código, com o intuito de impedir que o desenvolvedor escreva linhas extensas.

A grande maioria (ou todas) das linguagens de programação trazem vários recursos para evitar a concatenação composta. No código acima, exemplificado em Delphi, poderíamos separar as partes da instrução SQL com o comando Add da Query:

Query.SQL.Add('Insert into CLIENTES'); // Tabela
Query.SQL.Add('(Codigo, Nome, CPF, DataNasc, Cidade)'); // Campos da inserção
Query.SQL.Add('values (');
Query.SQL.Add(StrToInt(EditCodigo.Text) + ','); // Campo "Código"
Query.SQL.Add(QuotedStr(EditNome.Text) + ','); // Campo "Nome"
Query.SQL.Add(QuotedStr(EditCPF.Text) + ','); // Campo "CPF"
Query.SQL.Add(StrToDate(EditDataNasc.Text) + ','); // Campo "DataNasc"
Query.SQL.Add(QuotedStr(EditCidade.Text)); // Campo "Cidade"
Query.SQL.Add(')');

Embora uma única linha se transforme em várias, a interpretação do código fica bem mais nítida, possibilitando, inclusive, a utilização de comentários para elevar o nível de compreensão.

Porém, em alguns casos, o caracter “+” é a única alternativa para concatenar um texto, como a montagem de um filtro de consulta composta. Mesmo assim, ainda podemos dividir as concatenações em linhas:

sFiltro := 'Nome like ' + QuotedStr(EditNome.Text) + '%';
sFiltro := sFiltro + ' and Cidade = ' + QuotedStr(EditCidade.Text);
sFiltro := sFiltro + ' and Profissao = ' + QuotedStr(EditProfissao.Text);
sFiltro := sFiltro + ' and Sexo = ' + QuotedStr(EditSexo.Text);

 

Para mensagens, a concatenação fica ainda mais fácil. O Delphi disponibiliza o comando Format para gerar uma string substituindo parâmetros por valores:

sMensagem := Format('A venda nº %d foi registrada para o cliente %s.',
  [StrToInt(EditCodigo.Text), EditNome.Text]);
 
ShowMessage(sMensagem);

Moleza, né?

 

Surgiu alguma dúvida sobre os tópicos do artigo? Gostaria de complementar algo? Deixe um comentário! 🙂
Hoje fico por aqui, pessoal! Obrigado e até a próxima!


 

Compartilhe!
Share on FacebookTweet about this on TwitterShare on LinkedInShare on Google+Pin on PinterestEmail this to someone

6 comentários

  1. Bom dia, André.
    Já cometi esse erro de usar valores literais em vez de constantes ou ‘variáveis’. Há poucos dias tive um problema causado por isso, (if UF=”SP” then…) e se eu tivesse usado uma constante(UF_EMISSAO), teria poupado horas procurando as ocorrências em 3 programas (não imaginava vender fora de ‘SP’).
    Concordo com você, Aquele “desenvolvedor” que você citou está completamente errado. É muito melhor e mais legível usar uma constante ou variável, com um nome que identifica o conteúdo. Na minha opinião, o único tipo de aplicação que tem constantes imutáveis são de engenharia e científicas (PI, seno, cosseno, fatores de conversão, e outras regras). Para quem lida com elas no dia a dia, são fáceis de reconhecer, mas se a aplicação precisar mudar a precisão de “PI” (‘3,1415’ 4 decimais para ‘3,14159 26535’ 10 decimais) vai ter que procurar todas as referências e corrigir. Uma constante resolveria isso fácil. Eu uso variáveis com essa função, mas poderiam ser constantes, sendo que sempre carrego no inicio do programa como parâmetros. Acho que este tipo de código (“eu entendo, está bom”) é como você comentou em no artigo “Evite a propriedade do código (Code Ownership)”. O problema é pra quem tiver que alterar ou corrigir.
    No caso das “Query’s” e também filtros, faço exatamente como vc orientou, muito mais fácil e claro.
    Mais uma vez obrigado pela Orientação.
    Abraço.

    1. Olá, Gerson, como vai?
      Sem dúvidas, a utilização de constantes no projeto agiliza, e muito, a atividade de manutenção, além da questão da expressividade do código. Quanto mais o código poder “falar por si só”, melhor será a compreensão.
      Tem razão, Gerson, propriedades científicas e matemáticas possuem valores imutáveis mas, mesmo assim, ainda recomendo a utilização de constantes, já que nem todos os desenvolvedores têm conhecimento dos valores dessas propriedades. Por exemplo, ler a constante nVALOR_PI é mais nítido do que o número literal “3.14”.

      Obrigado novamente pelo comentário!

  2. Ola, André.
    Como eu disse, apoio o uso de constantes, e que o nome descreva o conteúdo. Mas qual a diferença em declarar ela como constante ou variável?
    Uso variáveis, que na verdade são constantes, pois só atribuo um valor na inicialização do programa (normalmente são parâmetros de configuração), raramente mudam, nunca durante a execução.
    Posso acrescentar que comentar o código também é boa pratica, principalmente procedures e funções. Às vezes me deparo com procedimentos que criei há 15 anos e penso: “O que isto está fazendo aqui. Pra que serve?”, então procuro fazer um comentário dentro dela e também procuro colocar um nome que identifique a sua utilidade, como “alinha_a_direita”, “centraliza” e “retira_acentos”. Ajuda bastante a depuração e entendimento do código.

    1. Bom dia, Gerson.
      A maior diferença entre variáveis e constantes são que essas últimas não permitem que o valor seja modificado.
      Já me deparei com situações em que valores de variáveis foram indevidamente alterados durante a execução da aplicação, gerando inconsistências. Ao trocar essas instâncias por constantes, o problema foi solucionado.
      Além disso, o uso de constantes também expressa que o valor é fixo, e não variável. Em um ambiente colaborativo de desenvolvimento, essa informação pode ajudar a compreender melhor o código.
      No Delphi, constantes podem ser declaradas com a palavra const.

  3. UM ORM não seria o mais indicado ? sim pois seria muito mais interessante e seguro que minha string sql refletisse exatamente os campos das minhas entidades…

    1. Lindemberg, a instrução SQL no artigo foi utilizada somente como exemplo.
      Mesmo assim, sim, ORM é uma boa alternativa para persistência de dados e poderia substituir o primeiro exemplo da concatenação composta.

      Abraço.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Preencha o campo abaixo * Time limit is exhausted. Please reload CAPTCHA.