Cheira porque provavelmente há muitos casos em que poderia ser editado ou melhorado.
A maioria desses cheiros são apenas indícios de algo que pode estar errado. Eles não são necessariamente consertados per se ... (Você deve investigar isso.)
Vamos continuar...
Não adicione IFs verificando o ambiente de produção.
TL;DR: Evite adicionar condicionais relacionados à produção
Às vezes, precisamos criar comportamentos diferentes no desenvolvimento e na produção.
Por exemplo, a força das senhas.
Nesse caso, precisamos configurar o ambiente com a estratégia de força e testar a estratégia e não o ambiente em si.
def send_welcome_email(email_address, environment): if ENVIRONMENT_NAME == "production": print(f"Sending welcome email to {email_address} from Bob Builder <[email protected]>") else: print("Emails are sent only on production") send_welcome_email("[email protected]", "development") # Emails are sent only on production send_welcome_email("[email protected]", "production") # Sending welcome email to [email protected] from Bob Builder <[email protected]>
class ProductionEnvironment: FROM_EMAIL = "Bob Builder <[email protected]>" class DevelopmentEnvironment: FROM_EMAIL = "Bob Builder Development <[email protected]>" # We can unit test environments # and even implement different sending mechanisms def send_welcome_email(email_address, environment): print(f"Sending welcome email to {email_address} from {environment.FROM_EMAIL}") # We can delegate into a fake sender (and possible logger) # and unit test it send_welcome_email("[email protected]", DevelopmentEnvironment()) # Sending welcome email to [email protected] from Bob Builder Development <[email protected]> send_welcome_email("[email protected]", ProductionEnvironment()) # Sending welcome email to [email protected] from Bob Builder <[email protected]>
Este é um cheiro de design.
Precisamos criar configurações vazias de desenvolvimento/produção e delegá-las com objetos polimórficos personalizáveis.
Evite adicionar condicionais não testáveis.
Crie configurações delegando regras de negócio.
Use abstrações, protocolo e interfaces, evite hierarquias rígidas.
Code Smell 56 - Pré-processadores
Foto de Birmingham Museums Trust no Unsplash
Este tweet foi inspirado por @ Jan Giacomelli
A complexidade é um sinal de imaturidade técnica. Simplicidade de uso é o sinal real de um produto bem projetado, seja um ATM ou um míssil Patriot.
Daniel T. Ling
Grandes Citações de Engenharia de Software
A reutilização de variáveis torna os escopos e limites mais difíceis de seguir
TL;DR: Não leia e escreva a mesma variável para propósitos diferentes
Ao programar um script é comum reutilizar variáveis.
Isso causa confusão e dificulta a depuração.
Devemos restringir o escopo o máximo possível.
// print line total double total = item.getPrice() * item.getQuantity(); System.out.println("Line total: " + total ); // print amount total total = order.getTotal() - order.getDiscount(); System.out.println( "Amount due: " + total ); // variable is reused
function printLineTotal() { double total = item.getPrice() * item.getQuantity(); System.out.println("Line total: " + total ); } function printAmountTotal() { double total = order.getTotal() - order.getDiscount(); System.out.println( "Amount due: " + total ); }
Os Linters podem usar a árvore de análise para encontrar definições e usos de variáveis.
Evite reutilizar nomes de variáveis. Use nomes mais específicos e diferentes.
Cheiro de código 03 - as funções são muito longas
Refatoração 002 - Método Extrair
Simplicidade antes da generalidade, uso antes da reutilização.
Kevlin Henney
Grandes Citações de Engenharia de Software
Afirmar que dois números float são iguais é um problema muito difícil
TL;DR: Não compare floats
Comparar números float é um antigo problema da ciência da computação.
A solução usual é usar comparações de limite.
Recomendamos evitar flutuações e tentar usar números de precisão infinita.
Assert.assertEquals(0.0012f, 0.0012f); // Deprecated Assert.assertTrue(0.0012f == 0.0012f); // Not JUnit - Smell
Assert.assertEquals(0.0012f, 0.0014f, 0.0002); // true Assert.assertEquals(0.0012f, 0.0014f, 0.0001); // false // last parameter is the delta threshold Assert.assertEquals(12 / 10000, 12 / 10000); // true Assert.assertEquals(12 / 10000, 14 / 10000); // false
Podemos adicionar uma verificação com assertEquals() em nossas estruturas de teste para evitar a verificação de flutuações.
Devemos sempre evitar comparar floats.
Code Smell 71 - Flutuadores Mágicos Disfarçados de Decimais
Foto de Mika Baumeister no Unsplash
Deus fez os números naturais; tudo o mais é obra do homem.
Leopold Kronecker
Grandes Citações de Engenharia de Software
O que acontece se você combinar 4 code smells?
TL;DR: Evite getters, evite setters, evite metaprogramação. Pense no Comportamento.
Setters e getters são uma má prática da indústria.
Muitos IDEs favorecem esse cheiro de código.
Algumas linguagens fornecem suporte explícito para construir modelos anêmicos e DTOs.
class Person { public string name { get; set; } }
class Person { private string name public Person(string personName) { name = personName; //imutable //no getters, no setters } //... more protocol, probably accessing private variable name }
Este é um recurso de linguagem.
Devemos evitar línguas imaturas ou proibir suas piores práticas.
Precisamos pensar bem antes de expor nossas propriedades.
O primeiro passo é parar de pensar em propriedades e focar apenas no comportamento.
Code Smell 70 - Geradores de modelos anêmicos
Cheiro de Código 01 - Modelos Anêmicos
Nada é mais difícil do que trabalhar com um prazo apertado e ainda reservar um tempo para limpar enquanto trabalha.
Kent Beck
Grandes Citações de Engenharia de Software
Padrão significa 'tudo o que ainda não sabemos'. Não podemos prever o futuro.
TL;DR: Não adicione uma cláusula padrão aos seus casos. Altere-o para uma exceção. Seja explícito.
Ao usar casos, geralmente adicionamos um caso padrão para que não falhe.
Falhar é sempre melhor do que tomar decisões sem provas.
Como o gabinete e os interruptores também são um cheiro, podemos evitá-los.
switch (value) { case value1: // if value1 matches the following will be executed.. doSomething(); break; case value2: // if value2 matches the following will be executed.. doSomethingElse(); break; default: // if value does not presently match the above values // or future values // the following will be executed doSomethingSpecial(); break; }
switch (value) { case value1: // if value1 matches the following will be executed.. doSomething(); break; case value2: // if value2 matches the following will be executed.. doSomethingElse(); break; case value3: case value4: // We currently know these options exist doSomethingSpecial(); break; default: // if value does not match the above values we need to take a decision throw new Exception('Unexpected case ' + value + ' we need to consider it'); break; }
Podemos dizer aos nossos linters para nos avisar sobre usos padrão, a menos que haja uma exceção.
Escrever código robusto não significa que precisamos tomar decisões sem evidências.
Cheiro de código 36 - instruções switch/case/elseif/else/if
Foto de Joshua Woroniecki no Unsplash
O custo de adicionar um recurso não é apenas o tempo necessário para codificá-lo. O custo também inclui a adição de um obstáculo à expansão futura. O truque é escolher os recursos que não lutam entre si.
John Carmack
Grandes Citações de Engenharia de Software
E isso é tudo por agora…
O próximo artigo explicará mais 5 code smells!