Melhorando a manutenibilidade das aplicações

Autores: Jefferson Carlos Martins e Manoel Flávio Leal

1. Introdução

Martin Fowler descreve a técnica de programação chamada Refactoring como sendo um caminho disciplinado de reestruturação de código-fonte, seja em programação estruturada ou Orientada a Objetos [1]. A principal idéia do Refactoring é fazer pequenas alterações no código com o objetivo de melhorar sua manutenibilidade, aumentando desta forma sua simplicidade, flexibilidade, clareza e desempenho. É importante ter sempre em mente que Refactoring não se trata de adicionar novo código ou novas funcionalidades, mas sim de melhorar os já existentes. Quando aplica-se as técnicas de Refactoring deve-se tomar alguns cuidados, pois ao alterar o código-fonte de um programa há um grande risco de introduzir novos erros, principalmente se estiver realizando uma manutenção não-estruturada, onde o único elemento disponível é o código-fonte e a atividade de manutenção inicia-se com uma penosa avaliação do código, freqüentemente complicada pela documentação interna ruim [2].

Também deve-se tomar cuidado para não confundir Refactoring com Design Pattern.

2. Médodos de Refactoring

Existem noventa e três métodos de Refactoring catalogados por Martin Fowler, e neste artigo serão demonstradas as principais e mais utilizadas técnicas de Refactoring [3].

2.1. Extract Method (extração de métodos)

Este é o principal método de Refactoring, pois é amplamente utilizado por desenvolvedores que começaram suas carreiras na era da programação estruturada e que tiveram de se adequar às técnicas de Orientação a Objetos para reduzir o tempo gasto no desenvolvimento dos projetos, facilitar a manutenibilidade dos programas e aumentar a reusabilidade de partes do código. O Extract Method nada mais é que a transformação de parte do código em um novo método, para aumentar a usabilidade de uma parte ou para aumentar a legibilidade das funções/procedimentos.

Este método deve ser aplicado quando ocorrerem duplicação de linhas de código ou quando as funções/procedimentos forem muito longos e/ou de difícil entendimento, exigindo muitos comentários no código. Mas lembrem-se de sempre colocar um nome para o novo método que explique sua utilidade.

Dicas para a aplicabilidade deste método:

1. Analise seu código periodicamente;

2. Quando encontrar partes de código que possuam grande semelhança entre eles, transforme-o em um método dinâmico (com parâmetros);

3. Quando copiar o código para dentro do novo método, procure por variáveis locais e parâmetros utilizados pelo código extraído;

4. Quando o código extraído utilizar variáveis locais do método de onde ele foi extraído, passe essas variáveis como parâmetros ao novo método;

5. Sempre execute testes em seu código para verificação de possíveis erros gerados.

2.2. Extract Class (extração de classes)

Este método é semelhante ao Extract Method, exceto pelo fato de que ele trata apenas com classes. O Extract Class parte do pressuposto: se uma classe for muito grande, deve-se transformá-la em duas ou mais classes.

Dicas para a aplicabilidade deste método:

1. Somente aplique este método para classes que não fazem parte de nenhum Design Pattern de seu projeto;

2. Não confunda o método Extract Class do Refactoring com Design Pattern;

3. Utilize este método para criação de classes abstratas que podem ser generalizadas ou especializadas por outras classes.

2.3. Inline Method (método na linha)

Este método é o inverso do Extract Method, pois ele prevê a substituição da chamada de uma função/procedimento pela sua implementação, ou seja, pelo código da função/procedimento.

O Inline Method é utilizado para eliminação de chamadas desnecessárias de funções/procedimentos, aumentando assim a performance da aplicação.

Dicas para a aplicabilidade deste método:

1. Quando sua aplicação possuir funções/procedimentos mal organizados dentro do código, aplique o Inline Method em todos eles, depois aplique o Extract Method para melhorar a legibilidade do código;

2. Verifique se o método não é polimórfico ou se as suas subclasses o especializam;

3. Aplique este método para eliminar funções pequenas que não sejam reutilizadas em outras partes do código.

2.4. Replace Inheritance With Delegation (substitua herança por instância)

Este método é utilizado quando uma subclasse usa apenas parte das funcionalidades da sua superclasse ou não necessite herdar dados da mesma, então cria-se um campo para a superclasse e ajusta-se os métodos apropriados para esta, removendo-se a herança.

Deve-se ter em mente que herança é uma técnica excelente, mas não se deve descuidar ao realizar heranças de uma classe, pois em muitos casos pode-se notar que utilizamos apenas alguns métodos ou atributos da superclasse.

Dicas para a aplicabilidade deste método:

1. Crie um campo na subclasse que se refere a uma instância da superclasse e remova a herança;

2. Mude cada método na subclasse para que use a instância da superclasse;

3. Verifique as chamadas de métodos da superclasse.

2.5. Replace Conditional With Polymorphism (substitua condições por polimorfismo)

Este método consiste em dividir uma função/procedimento que possua muitas condições dentro da mesma em duas ou mais funções/procedimentos.

Dicas para a aplicabilidade deste método:

1. Caso exista uma função/procedimento que possua várias condições para funcionamento, divida-a em outras funções/procedimentos, aumentando, assim, sua legibilidade.

3. Aplicando Técnicas de Refactoring

As técnicas de Refactoring devem ser aplicadas durante o ciclo de desenvolvimento de uma aplicação para que o software gerado no fim desse processo seja de fácil manutenibilidade e legibilidade, mas é aconselhável que se utilize essas técnicas na fase de manutenção, onde o desenvolvedor tem a oportunidade de: melhorar o código existente por meio de revisões do código-fonte, refazer parte de um programa para melhorar sua legibilidade e eliminar códigos duplicados para facilitar sua manutenibilidade.

Apesar das técnicas de Refactoring aumentarem a manutenibilidade do software gerado não devemos aplicá-las quando um código estiver muito complexo e o recomendável seja recomeçar do zero, ou quando o projeto estiver muito próximo de sua entrega final, nesse caso é recomendável se terminar o software e nas futuras versões aplicar as técnicas de Refactoring.

A maneira mais simples para a aplicabilidade das técnicas de Refactoring é renomear as variáveis e métodos para nomes que sejam auto-explicativos como, por exemplo, eliminar variáveis com o nome i, j, c, x, y, muito utilizadas para controle/contadores, pelos nomes cont (de contador) ou pos (de posição).

Outra forma seria a limpeza e rearranjo do código, ou seja, dividir grandes partes de códigos em funções/procedimentos e criar variáveis temporárias para a simplificação de expressões complexas.

3.1. Exemplo Prático

Em seguida é apresentado um exemplo de utilização das técnicas de Refactoring, onde foi utilizado parte de um código-fonte de um sistema Web, que antes de se aplicar as técnicas de Refactoring o mesmo possuía 2.710 linhas de código-fonte, sendo que ainda faltavam adicionar algumas funcionalidades e realizar manutenções em pequenos procedimentos. Após a aplicabilidade do Extract Method e do Inline Method, seguindo as orientações de como aplicá-las, esse código ficou com 1.587 linhas e isso depois de adicionar algumas funcionalidades e realizar as manutenções que faltavam.

Exemplo:

Código-fonte antes da aplicabilidade das técnicas de Refactoring

<script language=”JavaScript”>

function validarDados() {

valor1590 = docuent.frm.PercRetido400.value;

valor800 = document.frm.PercRetido200.value;

valor100 = document.frm.PercRetido100.value;

valor050 = document.frm.PercRetido050.value;

valor025 = document.frm.PercRetido025.value;

valor0125 = document.frm.PercRetido0125.value;

valor0062 = document.frm.PercRetido0062.value;

valorNR = document.frm.PercRetidoNR.value;

if (valor1590 == “”) {

alert (“Entrada obrigatória para o Diâmetro 15,90.”);

document.frm.PercRetido1590.focus();

return (false); }

if (valor800 == “”) {

alert (“Entrada obrigatória para o Diâmetro 8,00.”);

document.frm.PercRetido800.focus();

return (false); }

if (valorNR == “”) {

alert (“Entrada obrigatória para o campo %Retido no Diâmetro NR.”);

document.frm.PercRetidoNR.focus();

return (false); }

}

</script>

Neste caso foram renomeados todos os campos PercRetido para que ficassem seqüenciais, depois eliminou-se a duplicidade de linhas de código através do Extract Method transformando aproximadamente 70 linhas em apenas 8 linhas de código.

Código-fonte após a aplicabilidade das técnicas de Refactoring

<script language=”JavaScript”>

function validarDados() {

validarPercRetido();

...

}

function validarPercRetido() {

for (pos = 1; pos < 11; pos++) {

if (eval(“document.frm.PercRetido”+pos+”.value”) == “”) {

alert (“Entrada obrigatória para o campo %Retido.”);

eval(“document.frm.PercRetido”+pos+”.focus()”); return false;

}

}

}

</script>

4. Refactoring e Design Patterns

É necessário deixar claro ao leitor que Refactoring e Design Patterns são metodologias diferentes. A tabela abaixo apresenta de forma objetiva o que é necessário entender de cada uma das metodologias.

5. Conclusão

A documentação interna dos sistemas geralmente é ruim ou quase inexistente, pois muitos desenvolvedores estão mais preocupados em desenvolver rapidamente suas aplicações e mostrar produtividade, do que documentar as linhas de código, visando a manutenibilidade futura pelos seus colegas e até mesmo por si próprio.

As novas ferramentas de desenvolvimento com o aumento da demanda de software faz com que as organizações tendam a gastar grande parte de seus recursos humanos com a manutenção de softwares antigos, pois a vida útil do software é muito maior do que a do hardware, e é comum que os softwares sofram manutenções dos tipos: perfectiva (adicionar novas funcionalidades), adaptativa (adequar o programa a novas plataformas de software e hardware) e preventiva (prevenir possíveis erros, ex. bug do milênio). Com isso os desenvolvedores tornam-se escravos das aplicações que criaram, pois além de estar em evolução, existe o fato de as manutenções inferidas poderem introduzir novos erros.

Devido à demanda das manutenções, o que exigia grandes esforços das equipes, foram realizados estudos e catalogadas técnicas de manutenção durante a fase de codificação, chamadas agora de Refactoring.

As técnicas de Refactoring foram divididas em vários métodos para facilitar o entendimento dos desenvolvedores, bem como a comunicabilidade entre os mesmos.
Conclui-se que as técnicas de Refactoring devem ser aplicadas sempre que possível para facilitar o entendimento do código por outros desenvolvedores e com isso tentar diminuir o tempo gasto no entendimento do código.

6. Referências

1. FOWLER, M. et al. Refactoring: improving the design of existing code.[S.l]: Addison-Wesley, 2000.

2. PRESSMAN, R. S. Engenharia de software. São Paulo: Makron Books do Brasil, 1995. 879 p.

3. Refactorings in alphabetical order. Disponível em: <http://www.Refactoring.com/catalog/index.html> Acesso em: 08 out. 2003.