O processo de refactoring é uma atividade que realiza mudanças no código-fonte de uma aplicação, mas sem alterar o comportamento e propósito inicial do software. A técnica de refactoring deve ser aplicada quando o desenvolvedor realiza modificações estruturais no software, as quais afetem todas as referências a um recurso modificado, tais referências são encontradas nos arquivos de código-fonte da aplicação. Podemos entender as técnicas de refactoring como um processo de aperfeiçoamento do código-fonte de uma classe sob diversas perspectivas, tais como: clareza, otimização ou padronização de código.
A NetBeans [1][2] utiliza um módulo integrado a IDE para suporte ao processo de refactoring de aplicações. Esse módulo adota um framework que disponibiliza uma série de facilidades para implementação de modificações de código, desde a versão 4.0 da ferramenta [3]. O módulo é responsável por modificações que refletem alterações na arquitetura das aplicações desenvolvidas. A ferramenta NetBeans IDE utiliza um repositório de meta informações para manter informações acerca da arquitetura do software em desenvolvimento. A infra-estrutura do repositório de informações implementa a MOF (Meta Object Facility) proposta pela OMG (Object Management Group), trata-se de um modelo que visa a padronização de repositórios de metadados. O grupo Netbeans.org mantém um projeto denominado MDR (Metadata Repository) [4] que é responsável pela integração do modelo de repositório de metadados em ferramentas da plataforma NetBeans.
Entre as motivações do projeto MDR podemos encontrar:
- Concepção de um código-fonte claro e de fácil manutenção;
- Redução da complexidade da aplicação;
- Remoção de redundâncias desnecessárias;
- Propiciar a reutilização de código;
- Otimizar e melhorar a performance do software.
Todas as operações de refactoring são avaliadas pela NetBeans IDE, permitindo ao desenvolvedor uma visualização prévia das alterações do código e onde serão refletidas as mudanças antes da aplicação do processo. A tabela 1 apresenta uma breve descrição das operações suportadas atualmente pelo módulo de atualização de código provido pela ferramenta.
Operação | Descrição |
Rename | Permite a alteração do nome (identificador) de uma classe, variavel, ou método. A alteração é propaganda em todo código que realize alguma referência ao identificador alterado. |
Extract Method | Essa operação gera um novo método a apatir do código previamente selecionado pelo desenvolvedor através do editor de código-fonte integrado ao ambiente de desenvolvimento. |
Change Method Parameters | Modifica os parâmetros de um método, refletindo a mudança em todos os código que realizam chamada ao mesmo. |
Encapsulate Fields | Gera os métodos get e set para os atributos de uma classe, atualizando todas as referências aos atributos em códigos associados |
Pull Up | Move métodos ou campos para a superclasse da classe em questão. |
Push Down | Move classes internas, métodos, ou campos para a especialização da classe corrente. |
Move Class | Move uma classe para outro pacote ou classe. Dessa forma, todas as referências a classe em questão são atualizadas. |
Move Inner to Outer Level | Move uma classe interna para um nível acima da hierarquia de classes a qual a mesma faz parte. |
Convert anonymous class to Inner | Converte uma classe anônima a uma classe interna, especificando-se um identificador e um constructor. Todas as referências à classe anônima serão atualizadas. |
Extract Interface | Cria uma nova interface a partir de um método não público e estático selecionado em uma classe ou interface. |
Extract Superclass | Cria uma nova classe abstrata, modificando a classe corrente para que a mesma estenda a nova classe e move os métodos e atributos selecionados para a nova classe. |
Use Supertype Where Possible | Modifica todo código que referencia a classe selecionada (ou interface), referenciando o tipo de sua superclasse |
Safely Delete | Verifica se existe alguma referência a um determinado código (classe, interface, etc), caso não exista, remove o código. |
Estudo de Caso
Para exemplificação da utilização das operações de refactoring providas pela NetBeans IDE, será utilizada uma hierarquia que implementa o modelo de classes de contas bancárias que servirá como base para um estudo de caso. O diagrama de classes, apresentado através da figura 1, ilustra o modelo conceitual da hierarquia de contas bancárias.
Note, através da figura anterior, que uma determinada conta bancária pode ser simples. Neste caso, os objetos serão instanciados a partir da classe “Conta”, a qual define uma estrutura básica para todos os elementos do tipo conta bancária. A partir do modelo de conta inicial, serão derivadas as seguintes especializações: “Especial” e “Poupança”; uma conta do tipo especial incrementa uma nova característica que é o limite e realiza um polimorfismo do método debita, acrescentando um comportamento distinto de sua superclasse; já as contas do tipo poupança acrescentam a característica aniversário, a qual será utilizada para realização de uma operação particular a essa classe denominada corrige, a qual será utilizada para atualização do saldo da poupança. A Implementação em Java das estruturas abstratas apresentadas no modelo anterior, podem ser visualizadas através da figura 2, ilustrada logo abaixo.
Com base no modelo conceitual e sua implementação em Java, veremos a aplicação das operações providas pelo modúlo de refactoring. Dessa forma, as próximas seções descreverão e exemplificarão, em mais detalhes, cada operação descrita na tabela 1.
Rename
Conforme descrito anteriormente, podemos realizar operações para alteração do identificador de elementos de um código-fonte, tais como: classes, interfaces, atributos e métodos. Sendo assim, todas as referências ao elemento modificado deverão utilizar o novo identificador aplicado durante a operação “Rename”. Vejamos como aplicar o conceito, renomeando a classe “Pessoa” para “Indivíduo”. Para realização dessa operação, realize os seguintes passos:
- selecione o nome da classe, a partir do editor de código-fonte;
- clique com o botão direito do mouse sobre a mesma e no menu de contexto que surgirá selecione a opção refactor -> rename, veja a figura 3;
- Será apresentada a caixa de diálogo Rename, conforme figura 4;
- Se desejar alterar as referências em comentários, selecione “Apply Rename on Comments”;
- Se quiser uma visualização das alterações antes de realizar a operação, selecione “Preview All Changes”;
- Clique no botão Next para realizar a operação;
- Se a caixa “Preview All Changes” foi marcada, a caixa de diálogo ilustrada através da figura 5 será apresentada; caso contrário as alterações serão realizadas.
- Na caixa de diálogo de previsão das alterações, quando apresentada, o desenvolvedor poderá optar por cancelar a operação clicando em Cancel, ou então executar a operação clicando em “Do Refactoring”.
Veja que a renomeação da classe “Pessoa” será propagada à classe “Conta”, visto que uma conta implementa um objeto do tipo “Pessoa” através do identificador do titular da conta. A figura 5 elucida este conceito, podemos observar através da visualização prévia que a referência ao titular da conta também será modificada.
A operação “Rename” pode ser utilizada para outros recursos de uma classe ou interface, tais como: atributos e métodos. Para fixação desse tópico, altere o identificador do atributo nome descrito na estrutura da classe “Conta” e observe o efeito.
Encapsulate Fields
Uma outra operação interessante e muito útil na rotina do desenvolvedor é a Encapsulate Fields. Trata-se de uma operação que acelera o trabalho de codificação da estrutura de uma classe, pois a mesma gera automaticamente o código dos métodos get e set. Vejamos a seguir os passos necessários para a execução dessa operação. Para isso, utilizaremos a classe “Pessoa”, a qual ainda não apresenta os métodos get e set definidos em sua estrutura.
- Selecione o nome da classe através do editor de código-fonte;
- lique com o botão direito do mouse sobre a mesma e no menu de contexto que surgirá selecione a opção refactor -> Encapsulate Fields, veja a figura 6;
- Será apresentada a caixa de diálogo Encapsulate Fields, conforme a ilustração da figura 7;
- A tabela apresentada na caixa de diálogo monta uma lista de atributos da classe “Pessoa” e duas colunas, onde o desenvolvedor irá marcar se deseja que sejam gerados os métodos get e set respectivamente;
- Além disso, o desenvolvedor poderá definir o nível de visibilidade de cada atributo (public, private, protected ou default) através da opção “Field's Visibility”;
- O grau de visibilidade dos métodos get e set poderá ser definido através da opção “Accessor's Visibility”;
- A opção “Use Accessors Even When Field Is Accessible” irá modificar as referências aos atributos da classe quando marcada. Dessa forma, as referências serão modificadas para fazer uso dos métodos get e set gerados.
- Assim como na operação “Rename”, quando marcada a opção “Preview All Changes”, uma prévia será apresentada antes da efetivação da operação.
A figura 8 demonstra o resultado de “Encapsulate Fields”, onde pode-se observar o código de cada método get e set gerado automaticamente pela operação.
No próximo artigo daremos continuidade à apresentação das funções de refactoring providas pela NetBeans IDE, onde poderemos observar o conjunto de facilidades que irão auxiliar na estruturação do código-fonte de nossos softwares, bem como auxilar no processo de modificação dos elementos das nossas aplicações.