Reduzindo o Tamanho de Classes

a) Smell a ser reduzido/eliminado: Large Class

b) Ingredientes

Ferramentas

Métrica(s) associada(s): número de linhas de código (Source Lines of Code – SLOC)

Técnicas

Mecânica

  1. Identificar pontos onde o smell ocorre (PMD com Maven)
  2. Garantir que todas as ocorrências de código duplicado tenham um nível satisfatório de cobertura de testes (uso do JaCoCo)
  3. Caso a cobertura não esteja satisfatória, escrever testes unitários que garantam o comportamento (introduzir mais testes com JUnit, acompanhando sua cobertura com JaCoCo)
  4. Aplicar as técnicas de refactoring
  5. Rodar métricas novamente para verificar a melhoria
  6. Repita o passo 1

c) Aplicando a Receita no Projeto Spark

Base de código: Spark (http://www.sparkjava.com/)

Repositório utilizado: https://github.com/carlosaml/spark

Configuração de ferramentas

  • Maven utilizado para o build
  • PMD configurado para detectar métodos com mais de 50 linhas
  • Testes JUnit rodando atráves do Maven
  • JaCoCo plugado no Maven para geração de relatórios de cobertura de testes

Aplicação da Receita

Executando o build do Maven, o relatório do PMD acusa que a classe spark.route.SimpleRouteMatcher é muito grande duas vezes.

Como assim duas vezes? Ao olhar o código, percebe-se que a classe SimpleRouteMatcher possui uma classe interna privada, chamada RouteEntry. O primeiro passo então é mover essa classe para o seu próprio arquivo utilizando Move Class.

Rodando o PMD novamente, vemos que agora somente a classe RouteEntry é reportada, portanto focaremos nela a partir de agora.

Ao analisar a classe, nota-se que ambos os métodos chamados em cada uma condições são longos e, consequentemente, não de fácil entendimento.

if (thisPathSize == pathSize) {
return matchesSpecificPaths(thisPathList, pathList, thisPathSize);
} else {
return matchesWildCards(path, thisPathList, pathList, thisPathSize, pathSize);
}

Este é um caso onde Replace Type Code with State/Strategy pode ser aplicado, já que o comportamento da classe é diretamente afetado por um de seus membros, que neste caso específico é path.

Para isso, precisamos aplicar Extract Class em ambos os métodos boolean matchesWildCards(…) e boolean matchesSpecificPaths(…), extraindo cada um para sua classe específica, que chamaremos de WildCardMatcher e SpecificPathMatcher, respectivamente. Com isso, RouteEntry pode simplesmente delegar essas responsabilidades para alguma das duas classes, dependendo de quais os valores de seus atributos.

Com isso, o trecho de código inicialmente mostrado se torna:

if (thisPathSize == pathSize) {
return specificPathMatcher.matchesSpecificPaths(thisPathList, pathList, thisPathSize);
} else {
return wildCardMatcher.matchesWildCards(path, thisPathList, pathList, thisPathSize, pathSize);
}

O resultado disso são dois longos métodos a menos na classe SimpleRouteMatcher e, portanto, uma classe menor, mais simples e de melhor manutenção. O PMD concorda, pois nem SimpleRouteMatcher nem RouteEntry são relatadas como classes grandes.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s