Motivação

A busca por um código de qualidade deveria ser uma premissa comum a todo desenvolvedor, bem como presente no cronograma de qualquer projeto. Muitas vezes, no entanto, com prazos curtos e a busca por entregas cada vez mais rápidas, o código acaba perdendo qualidade e elevando o custo de manutenção.

Uma das opções que visam melhorar a qualidade de código é conhecida como Refactoring. Essa opção oferece um conjunto de técnicas que possibilitam melhorar o design do código sem alterar a lógica de negócio, isto é, o resultado desejado pelo cliente. Nesse artigo vamos focar na técnica Extract Class, catalogada no grupo de refatorações Moving Features Between Objects, cujo objetivo é auxiliar na decisão de onde colocar as responsabilidades de cada elemento do código, possibilitando, de forma controlada, trocas de responsabilidades entre classes/objetos.

Aqui, aprenderemos como utilizar essa técnica na linguagem Ruby, abordando como executar cada etapa para obter um código mais coeso e menos acoplado.

Exemplo de código a ser refatorado

O Extract Class normalmente é utilizado quando uma classe está com baixa coesão, isto é, possui responsabilidades que não deveria. Para corrigir esse problema de design, cria-se uma nova classe, e os atributos e métodos da classe inicial que não estejam relacionados à responsabilidade dessa classe são movidos para a nova. Na Listagem 1 temos um exemplo de uma classe que pode ser refatorada com essa técnica.


  01 class Funcionario
  02     attr_reader :nome
  03     attr_accessor :codigo_area_telefone
  04     attr_accessor :nro_telefone
  05
  06     def numero_telefone
  07           '(' + @codigo_area_telefone + ') ' + @nro_telefone
  08     end
  09 end
  
Listagem 1. Exemplo de código a ser refatorado com Extract Class.

Note que a classe Funcionario está pouco coesa, reunindo em um só lugar diferentes conceitos e responsabilidades, como possuir o nome de um funcionário e saber formatar o número de telefone (linhas 06 a 08). Pode-se imaginar a complexidade que essa classe acumulará à medida que mais atributos e comportamentos relacionados a funcionários forem adicionados a ela. Assim, conclui-se que essa classe é uma forte candidata a passar pela refatoração do tipo Extract Class.

Aplicando a refatoração

De forma geral, para realizar essa refatoração é preciso executar os seguintes passos:

  1. Definir como serão reorganizadas as responsabilidades da classe;
  2. Criar uma nova classe para “abrigar” as responsabilidades que deixarão de fazer parte da classe em refatoração. A partir dessas mudanças a antiga classe poderá receber um novo nome, para que melhor represente o seu objetivo;
  3. Criar, na classe antiga, uma referência para a nova, de forma a manter o comportamento desejado, porém, agora, utilizando os recursos da classe recém-criada;
  4. Utilizar a refatoração Move Field para cada atributo que necessite ser movido. A cada movimento deve-se testar para verificar se as funcionalidades continuam se comportando como anteriormente;
  5. Utilizar a refatoração Move Method, para cada método que necessite ser movido. A cada Move Method também devem ser realizados testes para verificar se o código continua funcionando normalmente;
  6. Avaliar se múltiplos clientes poderão acessar a classe criada. Se forem permitidos múltiplos clientes, a nova classe deve ser acessada por um objeto de referência ou por um Value Object imutável (padrão de projeto muito utilizado).

Após esse processo, tomando como base o exemplo da Listagem 1, pode-se obter o resultado apresentado na Listagem 2.


  01 class TelefoneNumero
  02     #Também foram realizadas as refatorações Move Field e Move Method
  03     #para trazer o método numero_telefone e os devidos atributos
  04     attr_accessor : codigo_area_telefone, :nro_telefone
  05
  06     def numero_telefone
  07           '(' + codigo_area_telefone + ') ' + nro_telefone
  08     end
  09 end
  10
  11
  12 class Funcionario
  13     attr_reader :nome
  14
  15     def initialize
  16           @telefonenumero = TelefoneNumero.new
  17     end
  18
  19     def numero_telefone
  20           @telefonenumero.numero_telefone
  21     end
  22
  23     def telefonenumero
  24           @telefonenumero
  25     end
  26 end
  
Listagem 2. Exemplo de código refatorado com Extract Class.

É possível verificar que foi criada a classe TelefoneNumero (linhas 1 a 9), responsável pelos dados e comportamento relacionados ao número de telefone. Já em Funcionario, foi criado um link para conectar essa classe à classe TelefoneNumero (linhas 19 a 21), de forma a obter o número de telefone completo.

As boas práticas da Orientação a Objetos sugerem que uma classe deve respeitar o princípio da responsabilidade única. No entanto, é comum que com o passar do tempo uma classe passe a receber novas operações e dados, tornando-se mais complexa e assumindo responsabilidades que não deveriam fazer parte do seu escopo. Quando isso for observado, deve-se considerar a possibilidade de aplicar a refatoração.