Refatoração - Explorando os recursos do Visual Studio

Você precisa estar logado para dar um feedback. Clique aqui para efetuar o login
Para efetuar o download você precisa estar logado. Clique aqui para efetuar o login
Confirmar voto
0
 (1)  (0)

Veja neste artigo o que é qual a importância da refatoração de código. Serão apresentados exemplos práticos na linguagem C#, além dos recursos de que o Visual Studio dispõe para auxiliar esse processo.

Introdução

Muitos programadores, principalmente quando ainda estão na fase inicial do aprendizado sobre programação, escrevem códigos que, quando vão ver posteriormente, nem eles mesmos entendem o porquê de terem escrito o código daquela forma. Falta de identação e organização estética em geral, códigos demasiadamente extensos para resolver uma tarefa simples, repetição de código desnecessariamente.

Esse tipo de prática é comum e se acentua quando o código é desenvolvido “às pressas”, ou seja, quando o responsável não dispõe do tempo necessário para dar atenção a “detalhes” como os citados acima. Outro fator que contribui para que o código “cheire mal” (do inglês bad smell), termo bem humorado introduzido por Kent Beck, um dos criadores da Programação Extrema (Xtreme Programing), é a falta de conhecimento técnico sobre as ferramentas que se está utilizando e não entendimento pleno do cenário real para o qual se desenvolve o código.

Em geral não é interessante manter esse tipo de problema no código, então nesse momento entra a refatoração. Neste artigo serão apresentados os principais conceitos sobre refatoração de código, com exemplos práticos na linguagem C#. Além disso, serão apresentados alguns recursos do IDE Visual Studio que auxiliam e tornam mais prática a refatoração.

Refatoração

A refatoração consiste em aprimorar a estrutura interna do código sem, contudo, alterar seu resultado externo. Essa melhoria deve ser tanto estética quanto organizacional e muitas vezes requer a reescrita total de certos blocos de código.

Apesar de parecer um pouco “retrabalho” e contrariar o princípio “em time que está vencendo, não se mexe”, ou seja, “se o código está funcionando, não precisa ser alterado”, os benefícios da refatoração são facilmente observados logo após sua execução.

Alguns sinais que podem indicar a necessidade de refatoração são:

  • Código duplicado (mesmo bloco de código repetido várias vezes);
  • Métodos e classes muito extensos (nesse casso também podem estar sendo contrariados princípios como o SRP - Single Responsibilty Principle, ou Princípio da Responsabilidade Única);
  • Lista de parâmetros muito extensa (o que provavelmente indica um método muito longo que deve estar fazendo mais do que deveria);
  • Má identação (código esteticamente desorganizado, o que dificulta a compreensão para quem o lê posteriormente).

Exemplos práticos

A partir deste ponto serão apresentados exemplos práticos de refatoração, considerando alguns dos pontos citados. Inicialmente será apresentado um código com problemas e, em seguida, o mesmo código refatorado.

Código repetido e métodos muito longos

Listagem 1: Exemplo de código repetido

public class Produto
{
    public string Descricao;
    public decimal Preco;
    public decimal Estoque;

    public void Salvar()
    {
        if (Descricao.Length > 50)
        {
            Console.WriteLine("A descrição deve ter no máximo 50 caracteres.");
            return;
        }

        if (Preco <= 0)
        {
            Console.WriteLine("O preço deve ser maior que zero.");
            return;
        }

        if (Estoque < 0)
        {
            Console.WriteLine("O estoque não pode ser negativo");
            return;
        }

        //Inserir produto no banco de dados
    }

    public void Atualizar()
    {
        if (Descricao.Length > 50)
        {
            Console.WriteLine("A descrição deve ter no máximo 50 caracteres.");
            return;
        }

        if (Preco <= 0)
        {
            Console.WriteLine("O preço deve ser maior que zero.");
            return;
        }

        if (Estoque < 0)
        {
            Console.WriteLine("O estoque não pode ser negativo");
            return;
        }

        //Alterar produto no banco de dados
    }
}

No código acima temos uma classe Produto com dois métodos, Salvar a Atualizar. Em ambos os métodos, antes de se efetuar a operação principal (inserção e atualização do produto na base de dados), são feitas verificações sobre suas propriedades, para garantir que elas seguem certas regras.

Nesse exemplo a classe possui apenas dois métodos, porém, em um sistema real possivelmente haveriam outras operações a serem implementadas e a classe possuiria mais atributos a serem verificados.

Então, refatorando esse código, poderíamos colocar toda essa verificação em um método e, sempre que fosse preciso avaliar os atributos, bastaria chamar esse método. Isso é feito na listagem a seguir, onde foi criado o método ValidarAtributos.

Listagem 2: Removendo código repetido usando método

public class Produto
{
    public string Descricao;
    public decimal Preco;
    public decimal Estoque;

    private bool ValidarAtributos()
    {
        if (Descricao.Length > 50)
        {
            Console.WriteLine("A descrição deve ter no máximo 50 caracteres.");
            return false;
        }

        if (Preco <= 0)
        {
            Console.WriteLine("O preço deve ser maior que zero.");
            return false;
        }

        if (Estoque < 0)
        {
            Console.WriteLine("O estoque não pode ser negativo");
            return false;
        }

        return true;
    }

    public void Salvar()
    {
        if(ValidarAtributos())
        {
            //Inserir produto no banco de dados
        }
    }

    public void Atualizar()
    {
        if (ValidarAtributos())
        {
            //Alterar produto no banco de dados
        }
    }
}

Caso a validação de um dos atributos falhe, o método ValidarAtributos retorna falso e, usando esse resultado, os métodos Salvar e Atualizar podem manter o foco no seu real objetivo.

Essa modificação na estrutura do código garante, pelo menos, dois benefícios facilmente observáveis:

  • Evita erros humanos como a não validação de um dos atributos em algum ponto, por esquecimento ou por cópia incompleta do código previamente desenvolvido, pois toda validação estará centralizada em um único método.
  • Evita repetição de trabalho futuramente. Por exemplo, caso a validação de um campo mude ou outro campo seja inserido, não será preciso fazer a mesma alteração em vários pontos do código, apenas no método de validação.

Observando bem, vemos que além do código repetido, também foi melhorada a questão do método muito longo, pois as operações foram divididas em partes com funções específicas.

Má identação

A má identação (organização das linhas de código “hierarquicamente”, ou seja, seguindo níveis de tabulação e espaçamento bem definidos que deixem claro o fluxo do código), além de deixar o código “feio”, é um dos principais agravantes para a dificuldade de compreensão do código posteriormente à escrita.

A listagem a seguir apresenta um exemplo de código totalmente desorganizado, sem identação nenhuma. Em seguida o mesmo código é organizado e apresentado com uma estética completamente diferente.

Listagem 3: Código sem identação

public class Pedido
{
public int Numero; public DateTime Data;
public void IniciarPedido()
{
Console.WriteLine("Informe o número do pedido:");
int num = Convert.ToInt32(Console.ReadLine());
if (num <= 0) { Console.WriteLine("Número inválido"); return; }
Numero = num; Data = DateTime.Today;
}
}

De fato todo código poderia ser escrito em uma única linha, poiso Visual Studio desconsidera os espaços em branco no código e o separador de expressões é o ponto-e-vírgula (;). Porém, essa não é nem de longe uma prática aconselhável, pois o próprio autor do código, muito provavelmente, terá dificuldade para fazer manutenção futuramente.

O próprio IDE Visual Studio ajuda a manter o código organizado, identando automaticamente expressões assim que são concluídas. Aproveitando esses recursos, podemos alterar o código anterior, deixando-o com a seguinte aparência:

Listagem 4: Código identado

public class Pedido
{
    public int Numero;
    public DateTime Data;

    public void IniciarPedido()
    {
        Console.WriteLine("Informe o número do pedido:");
        int num = Convert.ToInt32(Console.ReadLine());
        if (num <= 0)
        {
            Console.WriteLine("Número inválido");
            return;
        }
        Numero = num;
        Data = DateTime.Today;
    }
}

Agora está muito mais fácil compreender o código e a ordem em que é executado.

Recursos do Visual Studio para Refatoração

O poderoso IDE Visual Studio fornece nativamente alguns recursos para auxiliar o processo de refatoração do código, tornando algumas tarefas bastante simples. Selecionando um trecho de código, em uma classe, por exemplo, e clicando com a direita sobre ele, haverá a opção “Refactor” no menu apresentado, conforme a figura a seguir.

Menu Refactor

Figura 1: Menu Refactor

Obviamente cada item do submenu é aplicável a uma situação diferente, a seguir cada uma dessas opções é explicada individualmente.

Rename (F2)

A opção Rename, como o nome sugere, deve ser usada pare renomear um identificador, como um atributo, método ou classe. Tomando o exemplo da Listagem 2, posicionando o cursor sobre ou selecionando o nome do método ValidarAtributos e usando a opção Rename, podemos alterar o nome do método na sua declaração, bem como as referências a ele no restante do código.

A figura a seguir mostra a janela que se abre quando utilizamos essa opção, usando o exemplo citado.

Renomeando método

Figura 2: Renomeando método

Mantendo a opção “Preview reference changes” marcada, em seguida é apresentada uma janela com os pontos do código que fazem referência a esse elemento (no caso, o método).

Pré-visualização de mudanças no código

Figura 3: Pré-visualização de mudanças no código

Clicando sobre cada referência na parte superior é possível ver o trecho de código abaixo. É possível ainda escolher onde o nome do método deve ser alterado, marcando e desmarcando as referências, como visto acima.

Extract Method...(CTRL+R, M)

Essa opção permite extrair um método de um bloco de código selecionado, ou seja, selecionando um trecho de código, é possível torna-lo um método. Podemos tomar como exemplo a Listagem 1. Selecionando o trecho do método Salvar onde é feita a validação dos atributos da classe e usando a opção Extract Method, a seguinte janela é apresentada.

Extraindo método

Figura 4: Extraindo método

Deve-se então informar o nome do método e clicar em OK. O bloco de código selecionado será passado para o corpo do novo método e, onde antes estava esse código, será feita a chamada ao método criado.

Encapsulate Field...(CTRL+R, E)

O encapsulamento é uma das bases da Programação Orientada a Objetos e no Visual Studio não poderia ser diferente. Sabendo que o .NET Framework é composto por centenas de classes e que C# é uma linguagem orientada a objetos, o IDE facilita o encapsulamento de campos de uma classe, criando automaticamente os métodos get e set e as propriedades (públicas) que encapsulam cada atributo (privados).

Para testar essa funcionalidade, utilizemos o seguinte trecho de código, onde temos três atributos que deveriam ser privados, mas se encontram públicos devido a falta de encapsulamento (exemplo da classe Produto, vista anteriormente).

Listagem 5: Campos não encapsulados

public string _descricao;
public decimal _preco;
public decimal _estoque;

Os campos são os mesmo usados anteriormente na classe Produto, porém, os nomes dos atributos foram modificados, usando um caractere underscore (_) no início do nome, cuja primeira letra foi alterada para minúscula. Esse é um padrão muito utilizado para esse fim (encapsulamento). Enquanto isso, as propriedades que encapsulam esses campos devem ter o nome escrito normalmente (como estava nas Listagem 1 e 2).

Posicionando o cursor sobre um dos campos e usando a opção Encapsulate Field, a seguinte janela é apresentada.

Encapsulando campo

Figura 5: Encapsulando campo

Deixando marcada a opção “Preview reference changes”, ocorrerá o mesmo que na opção Rename, mostrando os trechos do código que fazem referência ao atributo e que agora usarão a propriedade que o encapsula.

As opções “Search in comments” e “Search in strings”, que também aparecem no menu Rename servem para substituir o valor alterado também nos comentários e strings ao longo do código.

Clicando em OK e repetindo o procedimento para os demais atributos, temos agora os campos encapsulados, conforme o código a seguir.

Listagem 6: Campos encapsulados

private string _descricao;

public string Descricao
{
    get { return _descricao; }
    set { _descricao = value; }
}

private decimal _preco;

public decimal Preco
{
    get { return _preco; }
    set { _preco = value; }
}

private decimal _estoque;

public decimal Estoque
{
    get { return _estoque; }
    set { _estoque = value; }
}

Agora o encapsulamento está feito, os atributos que estavam públicos se tornaram privados e foram criadas propriedades que os encapsulam.

Extract Interface... (CTRL+R, I)

Interfaces também são largamente utilizadas para padronizar classes. Então, caso se tenha uma classe e em algum momento seja observado que todos ou alguns de seus campos possam ser usados também por outras classes, pode-se extrair uma interface dela usando o menu Extract Interface.

Por exemplo, usando a classe Produto das primeiras listagens, podemos posicionar o cursos do mouse sobre o nome da classe e usar esse menu. Será apresentada uma janela com os dados da interface a ser extraída, conforme a figura a seguir.


Figura 6: Opções da interface extraída

Selecionando, por exemplo, todos os membros da classe Produto e clicando em OK, será gerado um novo arquivo (com o nome definido no campo “New file name”) contendo a interface cujo nome foi definido no primeiro campo da tela apresentada.

O código da interface é o seguinte:

Listagem 7: Interface extraída

interface IProduto
{
    void Atualizar();
    string Descricao { get; set; }
    decimal Estoque { get; set; }
    decimal Preco { get; set; }
    void Salvar();
}

A classe Produto passa automaticamente a implementar essa interface.

Remove Parameters...(CTRL+R, V)

Quando se tem um método com vários parâmetros e em algum momento é preciso remover um deles, pode-se utilizar o menu Remove Parameters para alterar a assinatura do método e as referências a ele.

Quando se remove um ou mais parâmetros de um método, todas as chamadas a ele são alteradas, adequando-se à nova assinatura.

Por exemplo, consideremos o seguinte método:

Listagem 8: Método com vários parâmetros

public void IniciarPedido(int numero, DateTime data)
{
        
}

Em determinado momento surgiu a necessidade de remover o segundo parâmetro, por exemplo. Então, usando o menu Remove Parameters, a seguinte tela é apresentada.

Removendo parâmetros de um método

Figura 7: Removendo parâmetros de um método

É possível remover um parâmetro clicando no botão Remove e, caso deseje desfazer a operação, basta usar o botão Restore e o parâmetro volta ao método. O botão Restore só funcionará antes de ser pressionado o botão OK, ou seja, caso a operação de remoção seja finalizada, não é possível voltar o parâmetro usando essa opção.

A opção “Preview reference changes” tem a mesma função que nos menus anteriores, permitindo visualizar previamente as chamadas ao método modificado e como elas ficarão após a remoção dos argumentos.

Reorder Parameters...(CTRL+R, O)

Este último subitem do menu Refactor permite alterar a ordem dos parâmetros de um método, modificando também as referências feitas e ele (chamadas).

Podemos usar o mesmo método da Listagem 9 como exemplo. Ao acionar o comando Reorder Parameters, a seguinte janela é apresentada.

Reordenando os argumentos de um método

Figura 8: Reordenando os argumentos de um método

Para alterar a posição de um argumento, basta selecioná-lo e usar as setas do lado direito da tela. Em seguida, deve-se clicar no botão OK para finalizar o processo.

Conclusão

Como vimos, a refatoração é um processo de fundamental importância no desenvolvimento de software. Aperfeiçoar a estrutura interna do código traz benefícios tanto a curto quanto a longo prazo, pois facilita a compreensão e manutenção do sistema.

Quando não se tem tempo bastante para implementar ou alterar uma funcionalidade, ou não se tem o correto e total entendimento da situação e requisitos que levaram a tal necessidade, é comum haver certa deterioração do código, ou seja, este vai tende a ficar cada vez mais “poluído” e desorganizado. Se desde o momento da concepção do sistema forem adotadas práticas como a refatoração de código, o impacto dessa deterioração será menor ao longo do tempo.

Vimos ainda exemplos práticos de refatoração na linguagem C# e os recursos que o IDE Visual Studio fornece para auxiliar esse processo, o que, entre outras coisas, o torna essa poderosa ferramenta de desenvolvimento que é.


 
Você precisa estar logado para dar um feedback. Clique aqui para efetuar o login
Receba nossas novidades
Ficou com alguma dúvida?