Code smells

Code Smell – Duplicação de Código

Hoje, vamos ver em Ruby como combater o Code Smell de duplicação de códigos usando um design pattern conhecido como Template Method. Identificando o Code Smell Um cenário muito comum ao desenvolvermos software é nos depararmos com a seguinte situação: uma quantidade significativa de código que resolve um problema X, mas que precisa sofrer pequenas alterações […]

Background Image

Hoje, vamos ver em Ruby como combater o Code Smell de duplicação de códigos usando um design pattern conhecido como Template Method.

Identificando o Code Smell

Um cenário muito comum ao desenvolvermos software é nos depararmos com a seguinte situação: uma quantidade significativa de código que resolve um problema X, mas que precisa sofrer pequenas alterações para resolver também o problema Y, Z e “n” outros problemas muito parecidos entre si.

Vamos supor que você esteja trabalhando em um projeto que precisa gerar boletos bancários para diversos bancos.

Ao olhar o código do sistema em produção, você percebe que  algum desenvolvedor no passado (que já saiu da empresa, afinal ninguém quer assumir a culpa) resolveu brilhantemente isolar o código para gerar boletos bancários para o Itaú em uma classe chamada BoletoItau – classe essa que parece com a seguinte:

class BoletoItau
  def initialize(valor, vencimento)
    @valor = valor
    @vencimento = vencimento
  end
  
  def gerar_boleto
    puts("Header do Boleto para o Banco Itau")
    puts("Valor: #{@valor}")
    puts("Vencimento: #{@vencimento}")
    puts("Footer do Boleto: Esse boleto deve ser pago no Banco Itau mais próximo")
  end
end

Parabéns para o desenvolvedor que resolveu isolar o código acima em uma classe. Mas, quando esse mesmo desenvolvedor teve que gerar um boleto para o banco Santander (que parece muito com o boleto do Itaú) ele fez algo muito, muito ruim: ele duplicou a classe BoletoItau, criando a BoletoSantander:

class BoletoSantander
  def initialize(valor, vencimento)
    @valor = valor
    @vencimento = vencimento
  end

  def gerar_boleto
    puts("Header do Boleto para o Banco Santander")
    puts("Valor: #{@valor}")
    puts("Vencimento: #{@vencimento}")
    puts("Footer do Boleto: Esse boleto deve ser pago no Banco Santander mais próximo")
  end
end

Agora você, que esta com a tarefa para criar boletos para o Banco do Brasil, se vê diante das opções: perpetuar esse legado de código duplicado, ou refatorar o código acima para algum design pattern existente.

Não se deixe seduzir pelo lado negro da força, gaste um tempinho e resolva permanentemente esse problema com o Template Method.

Identificando o que permanece igual

A chave central do Code Smell de código duplicado é identificar o que se repete entre os códigos semelhantes e isolá-los em uma classe que servirá de base para todas as subclasses.  Uma definição formal do pattern Template seria:

A idéia geral do Pattern Template Method é criar uma classe base abstrata com um método que sirva de esqueleto. Esse método possui o processamento que pode variar, realizando chamadas a métodos abstratos, que serão fornecidos por subclasses concretas.

Fonte: Design Patterns in Ruby

No exemplo anterior, vemos que as principais diferenças entre as classes BoletoItau e BoletoSantander se encontra no método gerar_boleto. 

Para transformar o método gerar_boleto em um Template Method, eu preciso antes transformar o código que ele executa em chamadas as métodos que serão sobrescritos por cada subclasse, que no meu exemplo, será cada Banco.

Uma proposta da classe Boleto seria:

class Boleto
  def initialize(valor, vencimento)
    @valor = valor
    @vencimento = vencimento
  end

  def gerar_boleto
    gerar_header
    gerar_valor
    gerar_vencimento
    gerar_footer
  end

  def gerar_header
  end

  def gerar_valor
    puts("Valor: #{@valor}")
  end

  def gerar_vencimento
    puts("Vencimento: #{@vencimento}")
  end

  def gerar_footer
  end
end

Com isso, a cada novo Banco, eu posso simplesmente estender a classe base Boleto e sobrescrever os métodos desejados. Os bancos poderiam ficar da seguinte forma:

# Banco Itaú
class BoletoItau < Boleto
  def gerar_header
    puts("Header do Boleto para o Banco Itau")
  end

  def gerar_footer
    puts("Footer do Boleto : Esse boleto deve ser pago no Banco Itau mais proximo")
  end
end

# Banco Santander
class BoletoSantander < Boleto
  def gerar_header
    puts("Header do Boleto para o Banco Santander")
  end

  def gerar_footer
    puts("Footer do Boleto : Esse boleto deve ser pago no Banco Santander mais proximo")
  end
end

# E finalmente o Banco do Brasil
class BoletoBancoBrasil < Boleto
  def gerar_header
    puts("Header do Boleto para o Banco do Brasil")
  end

  def gerar_footer
    puts("Footer do Boleto : Esse boleto deve ser pago no Banco do Brasil mais proximo")
  end
end

Don’t Repeat Yourself (DRY)

Code Smell
Photo by cottonbro studio on Pexels.com

A metodologia Don’t Repeat Yourself (DRY) é uma filosofia de programação que busca evitar a duplicação de código.

A ideia principal do DRY é que cada pedaço de conhecimento na programação deve ser representado por uma única, isto é, um única “entidade autoritativa”.

A implementação do princípio DRY pode ajudar a reduzir a complexidade e aumentar a manutenibilidade do código, pois elimina a necessidade de manter várias cópias do mesmo código, que podem acabar divergindo umas das outras.

Portanto, se qualquer parte do código precisar ser modificada, apenas precisará ser feito em uma única lugar, em vez de em vários lugares.

Por exemplo, algumas técnicas comuns para implementar o princípio DRY incluem:

  • Utilizar funções e procedimentos para encapsular lógicas comuns
  • Utilizar herança e polimorfismo para compartilhar comportamento entre classes
  • Utilizar bibliotecas e frameworks que forneçam funcionalidades comuns
  • Utilizar código genérico para evitar a criação de código específico para cada caso
  • Utilizar arquitetura modular para dividir seu código em módulos independentes e reutilizáveis

DRY é importante porque o código duplicado é uma fonte comum de problemas de manutenção e pode levar a bugs difíceis de encontrar.

Ao seguir o princípio DRY, você pode aumentar a confiabilidade do seu código para então torná-lo mais fácil de manter e evoluir no futuro.

Conclusão

O exemplo foi bastante reduzido para não tornar este post em uma dissertação acadêmica, mas adianto que  existe um mar de técnicas que podem ser aplicadas para evitar o Code Smell de duplicação de código.

Caso queira uma recomendação de livro, indico o Design Patterns – Elements of Reusable Object-Oriented Software, que é um livro mais abrangente sobre o tema.

Caso queria algo mais prático, tem o excelente Refactoring to Patterns que é um livro mais prático sobre Patterns. Em Ruby, existe o Design Patterns in Ruby, que usei como base para este post – e possivelmente usarei como base para os próximos posts.

Por fim, deixo também uma sugestão do clássico Clean Code, do Robert C Martin.