Eliminando código duplicado

a) Smell a ser reduzido/eliminado: Duplicated Code

b) Ingredientes

Ferramentas

Métrica(s) associada(s): número de tokens duplicados (Duplicate Code)

Técnicas

  • Testes unitários
  • Refactoring
    • Move Class/Field/Method
    • Extract Class/Method/Subclass/Superclass
    • Pull Up Method/Field/Constructor Body
    • Form Template Method
    • Consolidate Duplicate Conditional Fragments

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 log4j

Base de código: log4j (http://logging.apache.org/log4j/)

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

Configuração de ferramentas

  • Maven utilizado para o build
  • PMD configurado para detectar métodos com mais de 6 parâmetros
  • 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 CPD acusa três longos trechos de código, todas entre as seguintes classes:

  • org/apache/log4j/pattern/LogEvent.java
  • org/apache/log4j/spi/LoggingEvent.java

Olhando o relatório de cobertura do JaCoCo, podemos notar que ambas classes possuem cobertura de testes satisfatória, o suficiente para prosseguirmos com a receita.

Analisando as duas classes percebe-se que grande parte do comportamento é similar. Portanto, o objetivo será extrair uma classe base a partir das duas.

1o round: classe LogEvent

Aplicamos Extract Superclass, gerando a classe LogEventBase.

Em seguida, aplicamos Move Field nos seguintes campos:

  • logger
  • ndc
  • mdcCopy
  • ndcLookupRequired
  • mdcCopyLookupRequired
  • message
  • renderedMessage
  • threadName
  • throwableInfo

E Extract Method nos seguintes métodos:

  • getMessage()
  • getNDC()
  • getMDC(key)
  • getMDCCopy()
  • getRenderedMessage()
  • getThreadName()
  • getThrowableStrRep()

2o round: classe LoggingEvent

A classe passa a estender LogEventBase, criada no round anterior.

Devido a isso, métodos redudantes (idênticos em LoggingEvent e LogEventBase) podem ser simplesmente removidos de LoggingEvent.

3o round: novamente a classe LogEvent

Executamos Pull Up Field nos seguintes membros, movendo-os para LogEventBase:

  • TO_LEVEL
  • TO_LEVEL_PARAMS
  • methodCache
  • fqnOfCategoryClass
  • level
  • timeStamp
  • locationInfo

E também executamos Pull Up Method nos seguintes métodos:

  • readLevel(ois)
  • readObject(ois)
  • writeObject(ops)
  • writeLevel(ops)
  • setProperty(propName, propValue)
  • getProperty(key)
  • locationInformationExists()
  • getTimeStamp()
  • getPropertyKeySet()
  • getProperties()
  • getFQNOfLoggerClass()

4o round: classe LoggingEvent, novamente

Aplicamos Pull Up Field em:

  • startTime
  • categoryName

E Pull Up Method em:

  • getStartTime()
  • getLocationInformation()
  • getLevel()
  • getLoggerName()
  • getLogger()
  • getThrowableInformation()
  • removeProperty(propName)

Com isso eliminamos a duplicação entre as classes, visto que uma delas apenas necessita existir para manter compatibilidade com versões anterior devido ao número de caracteres no nome. Uma péssima razão para duplicar toda a classe.

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