Code Smell é uma realidade quando temos que programar pra ontem ou com um requisito volátil, nunca paramos de remendar os sistemas.

Refatorar é algo que deve vir com prioridade zero durante o desenvolvimento e que fica aparente a sua utilidade durante a escrita de testes. Metodologias como o TDD te obrigará a fazer refatorações constantemente.

Porém como que fazemos uma refatoração, quais os cuidados que devemos tomar, os caminhos a seguir?

Não existe uma receita de bolo e sim muita análise lógica do que está sendo modificado tomando todos os cuidados para não alterar o comportamento do sistema.

A intenção principal é a de não permitir que o sistema degrade durante o ciclo de vida existente em qualquer aplicação.

Code Smell

Code Smell

Code Smell

Primeiramente devemos identificar onde nosso código “cheira mal” ou seja ele permite que apliquemos algum tipo de refatoração.

Existem atualmente métricas de código que permite identificar com maior facilidade lugares para refatorar.

Um dos principais bad smell aparentam na complexidade de algumas classes, isso significa maior dificuldade de manutenção, maior propensão a erros, maior dificuldade em escrever testes e até mesmo impossibilidade de reaproveitamento da classe.

A parte mais dificil é identificar quais métodos estão com baixa pontuação métrica e qual estratégia tomar para um refactor decente.

As métricas no visual studio

Métricas no VIsual Studio

Métricas no VIsual Studio

 

As métricas geradas pelo visual studio nos dão insumos para tomada de decisões em cima do código desenvolvido. Cada item apresentando possuem diferentes situações e devem ser analisados de forma isoladas.

Maintainability Index – é um numeral de 0 a 100 que indica a relativa facilidade na manutenção do código. Quanto mais alto significa maior a facilidade na manutenção. Existe uma classificação por ícone nesta métrica que permite saber a zona com maior dificuldade de manutenção rapidamente.

Cyclomatic Complexity – é um numeral de que começa do 1 a maior que 51, quanto mais próximo do um menor a complexidade também na manutenção, porém maior complexidade na capacidade de testes, tendo então maior probablidade de erros acontecerem e de maior chance de refatoração.  A base de calculo desta métrica se sobressai nos laços e condicionais existentes, sendo que até 10 pontos de CC é considerado um código simples.

Depth of Inheritance – Este numeral traz o nível que a classe está em relação a árvore de herança. Toda classe inicia com o valor 1 por herdarem da System. Quanto mais este valor se aproximar de 1 é melhor, sendo assim, quanto maior ao valor 1 o nível de herança da classe analisada é maior, isto significa, segundo está análise, que terá o numero de métodos herdados muito grande e dificultando prever seu comportamento. Esta classificação pode ser redundante, pois o menor número de heranças implica, que seu código está pouco reutilizado.

Class Coupling –  Esta métrica traz o nível de acoplamento que suas classes possuem, quanto menor melhor. Indicam que o nível até 9 seja o ideal, porém não existe um consenso real, afinal tudo depende da complexidade do seu projeto.

Lines of Code – São as linhas totais encontrada na classe e em maior detalhe ele mostra as linhas de um método, o que também possuí uma análise que é quanto menor o linha do método menos cheiro ele isala, indica que o método está sendo objetivo com sua rotina, quanto mais linhas maior dificuldade de testes e entendimento.

Outras importâncias que devemos sempre lembrar enquanto desenvolvemos.

Código duplicado tem cheiro ruim, identifica-los é complicado também existem plugins que podem te ajudar a identificar e sanar este problema.

Comentários dentro do código indicam mal cheiro pois você não precisa explicar o que seu código está fazendo, ele deveria ser auto explicativo.

Regions são consideradas mal cheiro também pois dificultam a clareza na leitura do código, geralmente eles veem minimizados quando não estão com regions dentro de outros sem necessidades.

Códigos mortos, o r# e agora o visual studio 2015 ajudam neste lado, código mortos são variáveis, métodos, namespaces que estão ali mas não são usadas em nenhum momento, agora eles vem com a cor esboçada para facilitar na identificação e remoção.

O Refactor

Vamos a alguns casos típicos de programação do dia a dia e possíveis resoluções para diminuir o cheiro.

Console.WriteLine("Digite seu nome: ");
var nome = Console.ReadLine();

Console.WriteLine("Digite seu sobrenome: ");
var sobreNome = Console.ReadLine();

try
{
   Console.WriteLine(nome + " " + sobreNome);
   Console.Read();
}
catch { }

int valor = (nome + sobrenome).Length;

if (valor == 0)
{
  if (valor < 10)
  {
   if (valor > 10 && valor <= 20)
   {
    if (valor > 10 && valor <= 20)
    {
     if (valor > 10 && valor <= 20)
     {
      // código aqui
     }
    }
   }
  }
}

Este exemplo não possuí muita redundância, porém temos um código difícil de testar, podemos aplicar algumas formas de melhorar a legibilidade e com polimorfismo diminuir a complexidade ciclomática.

Poderíamos reescrever desta forma:

string nome, sobrenome;
LerNomeSobrenome(out nome, out sobrenome);
Console.WriteLine(nome + " " + sobrenome);
Console.Read();
private static void LerNomeSobrenome(out string nome, out string sobrenome)
{
  Console.WriteLine("Digite seu nome: ");
  nome = Console.ReadLine();
  Console.WriteLine("Digite seu sobrenome: ");
  sobrenome = Console.ReadLine();
}

 

E as condicionais, poderíamos usar Guard Clauses para evitar o Arrow Code

if (valor != 0) return;
if (valor > 10) return;
 if (valor < 10)
 {
  if (valor < 10 && valor := 20) return;
  if (valor > 10 && valor <= 20)
  {
   if (valor < 21 && valor >= 30) return;
    // código aqui
   }
  }

O refactor é bastante complicado e exige além da análise lógica muita experiência do desenvolvedor para não trocar uma implementação por outra pior ainda.

Refatorar switch case

Mais um exemplo de code smell que pode ser melhorado.

public string ReturnName(string name, string value)
{
  var _valueProcessor = new ValueProcessor();
 switch (name)
 {
  case "João":
   return _valueProcessor.Joao (value);
  case "Marcos":
   return _valueProcessor.Marcos(value);
  case "Marcia":
   return _valueProcessor.Marcia(value);
  case "Jason":
   return _valueProcessor.Jason(value);
  case "Tony":
   return _valueProcessor.Tony(value);
  default:
   return string.Empty;
 }
}

Neste caso todas as informações estão dentro de uma classe.

Ao invés disso juntar tudo em uma lista facilita e centraliza.

public Dictionary<string, Func<string, string>>; CreateDictionary()
{
  var dictionary = new Dictionary<string, Func<string, string>>
   {
    {"João", _valueProcessor.Joao},
    {"Marcos", _valueProcessor. Marcos },
    {"Jason", _valueProcessor.Jason},
    {"Marcia", _valueProcessor.Marcia},
    {"Tony", _valueProcessor.Tony}
  };
 return dictionary;
}

public string ReturnName(string name, string value)
{
  if (_dictionary.ContainsKey(name))
  {
   return _dictionary[name].Invoke(value);
  }
 return string.Empty;
}

Acredito seja isso, o refactor tem diversas frentes e possibilidades mas nunca um padrão único, use seu feeling, sua experiência, patterns e muitos code smell deixarão de existir.

Nadador de maratonas aquática aposentado, Pai, Programador .net e do que estiver por ai. Trabalho a alguns anos, grande parte deste fazendo “projetinhos”. Assumido um programador, não gosta de ver a glória sem participar da guerra. Sonhando um dia acertar no projeto perfeito e viver eternamente sendo dono da sua própria intelectualidade.

Facebook Twitter LinkedIn Google+ Skype 

Comentários

comentarios