Saturday, September 24, 2005

Refactoring

Dia desses, eu estava perdido na web, lendo sobre refactoring. Para quem está envolvido com desenvolvimento ágil, o vocábulo já paz parte do jargão diário e quem pratica XP provavelmente não consegue imaginar como existe gente que não usa a técnica deliberadamente.

O novato desavisado tende a reduzir tudo que escuta ao que lhe parece a essência da coisa, para simplificar e tentar entender o assunto. Falar em 'melhorar a estrutura do código sem modificar seu comportamento externo' pode parecer a este novato uma mera desculpa para ficar mimando o código, dando mais atenção a ele do que o estritamente necessário. Refatorar está, aparentemente, ligado a 'consertar o que não está quebrado'.

Aparentemente.

Na verdade, não tem nada a ver com isso. Refactoring é um dos processos que nos permite evitar ter todas as idéias sobre a arquitetura do sistema no início do desenvolvimento. Refactoring permite modificar o design do software segura e rapidamente quando necessário (e somente quando necessário). Com refactoring, podemos nos permitir aprender sobre o sistema ao mesmo tempo que o desenvolvemos. Não precisamos nos limitar a soluções pré-fabricadas (mas podemos fazer uso delas), muito menos projetar todo o sistema antes de começar a codificar.

Vantagens demais para ser verdade? Acho que não. Na verdade já fazemos isto todos os dias. Fazemos isto, por exemplo, quando identificamos uma lógica de execução comum para duas rotinas e dela extraímos uma terceira. Ou então quando movemos responsabilidades de uma classe para outra, como mostra Martin Fowler neste exemplo do seu 'Refactoring: Improving the design of existing code' (que já possui tradução para nosso querido idioma pátrio).

Digamos que foi feito um sistema para uma locadora de filmes (sim, Martin Fowler também tem seus momentos de baixa criatividade). Este sistema foi feito por uma equipe ainda não iniciada às artes do refactoring e que também não entendia muito de herança e polimorfismo na época. O cálculo de débito do cliente é calculado na classe Customer com auxílio do seguinte método privado, responsável pelo cálculo de débito proveniente de uma única locação.

class Customer {
  ...
  private double amountFor(Rental aRental) {
    double result = 0;
    switch (aRental.getMovie().getPriceCode()) {
      case Movie.REGULAR:
        result += 2;
        if (aRental.getDaysRented() > 2)
          result +=
            (aRental.getDaysRented() - 2) * 1.5;
          break;
      case Movie.NEW_RELEASE:
        result += aRental.getDaysRented() * 3;
        break;
      case Movie.CHILDRENS:
        result += 1.5;
        if (aRental.getDaysRented() > 3)
          result +=
            (aRental.getDaysRented() - 3) * 1.5;
        break;
    }
    return result;
  }      
  ...
}

Este método é utilizado por outro maior que calcula todo o débito do cliente. A equipe que desenvolveu o sistema original teve ao menos o cuidado de extrair este método para evitar que o maior ficasse exageradamente grande, mas não notaram que o novo método na verdade não pertence à classe Customer. Note que o método só mexe com atributos da classe Rental e pertence logicamente a ela. Podemos mover o método para ela e chamar apenas rental.getCharge() no local original sem perder o significado.

Depois disso, é hora de transformar esse switch altamente suscetível a erros em uma chamada de método da classe Movie, talvez utilizando o padrão State. Refactoring é a simples aplicação de modificações pequenas como estas de modo controlado. Acho que já deu pra pegar a idéia.

Talvez tenha alguém dizendo: "Mas isso não pode dar certo! As mudanças são pequenas demais para serem signiticativas."

Sim, as mudanças são pequenas. A força delas não vem da utilização isolada, mas contínua. Aqui o todo é maior do que a soma das partes, por causa da interação entre as várias mudanças.

Mas não nos limitemos a alguns exemplos. Fazer isso seria agir como o novato do qual falamos. Começar a aplicar refactoring na prática é tão fácil quanto não se limitar a duplicar um trecho de código deliberadamente, mas capturar a duplicação e escrever o caso geral. Isto pode se concretizar na extração de uma rotina, na identificação da oportunidade de usar um design pattern ou de muitos outros modos que vamos descobrindo quando tentamos.

2 Comments:

At 6/23/2006 07:08:00 PM, Anonymous Anonymous said...

Tenho lido alguns dos teus artigos e compartilho de muitas das idéias e abordagens que colocas. Esta visão do refactoring por exemplo, me parece bem coerente com o que se espera do uso da técnica. Muito bem!

 
At 6/19/2008 10:34:00 AM, Anonymous Anonymous said...

Farei um workshop sobre o assunto, seu texto foi de grande auxílio.

 

Post a Comment

<< Home