Deixando o código mais simples com refatoração no Java

(Last Updated On: 30 de setembro de 2017)

É muito comum no nosso dia a dia, como desenvolvedores, lidarmos com códigos escritos por outros desenvolvedores, seja para compreender uma regra de negócio como também para a evolução do programa de modo geral.

Entretanto, em algumas situações, essa rotina pode se tornar um pouco desafiadora justamente pela dificuldade em compreender o que um determinado código significa.

Para compreendermos melhor essa situação, vamos considerar uma aplicação Java para um estoque de produtos. Nessa aplicação temos o seguinte código:

public class ProdutoDAO {

    public List<Produto> lista(){

        // Implementação qualquer que devolve uma lista de produtos

    }

    public void produtosResumido(){
        List<Produto> prs = lista();
        for (Produto p : prs) {
            String dse = p.getDescricao().trim();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < dse.length(); i++) {
                if(i > 15) {
                    sb.append("...");
                    break;
                }
                sb.append(dse.charAt(i));
            }

            String da = sb.toString();
            String vf = p.getValor().toString()
								.replace(".", ",");
            String df = p.getId() + " - " + da + " - R$ " + vf;
            System.out.println(df);
        }
    }
}

Observe que se trata de uma classe DAO para produtos. Para esclarecer mais o exemplo, vamos considerar o seguinte model para a classe Produto:

public class Produto {

    private Integer id;
    private String descricao;
    private Double valor;

    // Construtores, getters, setters e métodos

}

Ao executarmos o método produtosResumido() temos o seguinte resultado:


1 - Refrigerante de ... - R$ 5,0
2 - Salgado Doritos ... - R$ 3,5
3 - Pão de forma Pre... - R$ 4,5
4 - Leite Longa Vida... - R$ 2,75
5 - Suco Tang 30g - R$ 4,0

Observando o resultado, temos um resumo dos produtos salvos no estoque. Porém, olhando rapidamente a implementação do método produtosResumido() é fácil compreender que esse resultado seria apresentado? A princípio, não…

Entendendo a refatoração de código

Em outras palavras, observe que entramos em uma situação na qual faz todo o sentido parar e pensar em maneiras para melhorar o nosso código atual, certo?

Esse processo de melhoria de código é conhecido tecnicamente como code refactoring, ou em português, refatoração de código.

Essa técnica, de modo geral, é constituída por diversas práticas que visam a melhoria no código nos seguintes pontos:

  • Simplicidade: minimizar a quantidade do código.
  • Legibilidade: melhorar a leitura do código para nós humanos.
  • Performance: aumentar a velocidade de execução.

A princípio você pode estar pensando:

“Refatoração é bem bacana, mas se eu fizer isso estarei modificando o comportamento da minha aplicação!”

A primeira impressão que temos durante esse tipo de processo é imaginar que a nossa aplicação vai ser modificada e vai apresentar comportamentos diferentes!

Isso é bem comum mesmo, porém, a ideia da refatoração é aplicar técnicas que modificam apenas o aspecto visual do código, ou seja, o comportamento inicial ainda é mantido!

Parece muito bom pra ser verdade, né? Sendo assim, vamos aplicar algumas técnicas de refatoração no código que vimos inicialmente e veremos a mágica acontecer!

Renomeando variáveis

A primeira das técnicas que iremos aplicar será a renomeação das variáveis. A princípio parece besteira, porém, vamos dar uma olhada em um trecho do nosso código:

List<Produto> prs = lista();
for (Produto p : prs) {
    String dse = p.getDescricao().trim();
    // restante do código
}

Pra você, psr, p, dse possuem algum significado? Pra mim e para qualquer pessoa que vai ver esse código pela primeira vez, não tem significado algum olhando à primeira vista, então que tal fazermos a seguinte modificação:

List<Produto> produtos = lista();
for (Produto produto : produtos) {
    String descricaoSemEspaco = produto.getDescricao().trim();
    // restante do código
}

Veja que agora é nítido que estamos lidando com umas lista de produtos, e para cada produto de produtos estamos pegando uma descricaoSemEspaco.

Note que até pra ler fica mais fácil! Em outras palavras, durante o processo de refatoração, renomear variáveis facilita a leitura do código. Portanto, quanto mais significativo for o nome da variável, mais fácil será a leitura. Vamos ajustar nos demais pontos:

public void produtosResumido(){
    List<Produto> produtos = lista();
    for (Produto produto : produtos) {
        String descricaoSemEspaco = produto.getDescricao().trim();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < descricaoSemEspaco.length(); i++) {
            if(i > 15) {
                sb.append("...");
                break;
            }
            sb.append(descricaoSemEspaco.charAt(i));
        }
        String descricaoAjustada = sb.toString();
        String valorFormatado = produto.getValor().toString()
						.replace(".", ",");
        String descricaoFinal = produto.getId() + " - " 
						+ descricaoAjustada + " - R$ " + valorFormatado;
        System.out.println(descricaoFinal);
    }
}

Observe que ainda foi mantida a variável sb referente à instância da classe StringBuilder. Por ser uma instância de uma classe compreendida pela maioria dos desenvolvedores e por não ter um nome tão enxuto, não há problema em deixar como sb.

Em outras palavras, para casos em que estamos fazendo uma instância de uma classe com um nome grande podemos usar uma abreviação. Nesse caso do StringBuilder poderíamos até mesmo usar o builder que ele teria significado o suficiente.

Lembre-se: Durante esse processo de renomear as variáveis, o mais importante é que no momento que for ler o código seja feita uma leitura fácil e objetiva, ou seja, mesmo para quem não conheça tanto da implementação compreenda apenas lendo.

Extração de método

Embora o nosso código tenha melhorado significamente a leitura, isto é, com nomes mais claros para nós humanos, ainda sim ele está um tanto quanto complexo. Se observarmos, temos uma boa quantidade de linhas dentro de apenas um único método!

Levando em conta essa situação, o que podemos fazer para resolver esse detalhe? Para esse tipo de cenário, podemos aplicar a técnica conhecida como extração de código. Agora você deve estar pensando:

“Legal, mas como funciona essa técnica?”

Basicamente, durante a leitura do código, fazemos uma análise e verificamos o que um conjunto de código faz, como por exemplo esse trecho:

String descricaoSemEspaco = produto.getDescricao().trim();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < descricaoSemEspaco.length(); i++) {
    if(i > 15) {
        sb.append("...");
        break;
    }
    sb.append(descricaoSemEspaco.charAt(i));
}
String descricaoAjustada = sb.toString();

Se observarmos com bastante atenção, todo esse código tem como objetivo ajustar uma descrição de um produto, certo? Então, que tal transformarmos todo esse código nesse método aqui?

private String ajustaDescricao(Produto produto) {
    String descricaoSemEspaco = produto.getDescricao().trim();
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < descricaoSemEspaco.length(); i++) {
        if(i > 15) {
            sb.append("...");
            break;
        }
        sb.append(descricaoSemEspaco.charAt(i));
    }
    return sb.toString();
}

Então basta apenas realizarmos a seguinte chamada dentro do nosso código anterior:

public void produtosResumido(){
    List<Produto> produtos = lista();
    for (Produto produto : produtos) {
        String descricaoAjustada = ajustaDescricao(produto);
        String valorFormatado = produto.getValor().toString()
						.replace(".", ",");
        String descricaoFinal = produto.getId() + " - "
						+ descricaoAjustada + " - R$ " + valorFormatado;
        System.out.println(descricaoFinal);
    }
}

Veja como o nosso método diminuiu! Fica até mais fácil de ler dessa maneira, podemos até mesmo fazer isso para outros pontos do código. Por exemplo este aqui:

String valorFormatado = produto.getValor().toString()
		.replace(".", ",");

Basicamente o código à direita formata o valor de acordo com a moeda, certo? Nesse caso nada nos impede de extrair um método aqui também, que tal deixarmos assim:

private String formataMoeda(Produto produto) {
    return produto.getValor().toString()
				.replace(".", ",");
}

Aproveitando, podemos também aplicar o mesmo conceito no trecho de código em seguida:

String descricaoFinal = produto.getId() + " - " 
		+ descricaoAjustada + " - R$ " + valorFormatado;

Vamos extrair para:

private String resumeDescricao(Produto produto, 
		String descricaoAjustada, String valorFormatado) {
    return produto.getId() + " - " 
				+ descricaoAjustada + " - R$ " + valorFormatado;
}

Agora vamos ver como fica aquele nosso método inicial:

public void produtosResumido(){
    List<Produto> produtos = lista();
    for (Produto produto : produtos) {
        String descricaoAjustada = ajustaDescricao(produto);
        String valorFormatado = formataMoeda(produto);
        String descricaoFinal = resumeDescricao(produto, 
						descricaoAjustada, valorFormatado);
        System.out.println(descricaoFinal);
    }
}

Bem mais fácil de ler, certo? Podemos facilitar mais ainda, ou seja, ao invés de enviar 3 parâmetros para criar a descrição final, podemos enviar apenas o produto e chamar os demais método dentro do método resumeDescricao():

public void produtosResumido(){
    List<Produto> produtos = lista();
    for (Produto produto : produtos) {
        String descricaoFinal = resumeDescricao(produto);
        System.out.println(descricaoFinal);
    }
}

private String resumeDescricao(Produto produto) {
    String descricaoAjustada = ajustaDescricao(produto);
    String valorFormatado = formataMoeda(produto);
    return produto.getId() + " - " 
				+ descricaoAjustada + " - R$ " + valorFormatado;
}

Veja o quanto o nosso código modificou:

public void produtosResumidos(){
    List<Produto> produtos = lista();
    for (Produto produto : produtos) {
        String descricaoFinal = resumeDescricao(produto);
        System.out.println(descricaoFinal);
    }
}

private String resumeDescricao(Produto produto) {
    String descricaoAjustada = ajustaDescricao(produto);
    String valorFormatado = formataMoeda(produto);
    return produto.getId() + " - " 
				+ descricaoAjustada + " - R$ " + valorFormatado;
}

private String formataMoeda(Produto produto) {
    return produto.getValor().toString()
				.replace(".", ",");
}

private String ajustaDescricao(Produto produto) {
    String descricaoSemEspaco = produto.getDescricao().trim();
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < descricaoSemEspaco.length(); i++) {
        if(i > 15) {
            sb.append("...");
            break;
        }
        sb.append(descricaoSemEspaco.charAt(i));
    }
    return sb.toString();
}

Agora o nosso método produtosResumidos() ficou bem mais fácil de compreender, sabemos logo de cara que ele passa por cada um dos produtos, pega uma descricaoFinal e faz uma impressão da mesma.

Conclusão

Veja que mesmo o código parecendo complexo apenas aplicando algumas técnicas de refatoração fomos capazes de melhorar e muito o aspecto de leitura e compreensão do nosso código, no caso, vimos a técnica de extração de método e renomeação de variáveis para nomes mais amigáveis.

Já passou por esse mesmo problema e não sabia como lidar? Então compartilhe comigo nos comentários 🙂

Quer aprender mais técnicas de refatoração? Então dê uma olhada no nosso curso de refatoração na prática com Java no qual aborda tanto as técnicas que vimos no post como também algumas outras que fazem uma grande diferença nesse processo de melhoria de código.

Fique por dentro

Content Editor at Alura and Software Developer

  • Cesar Lemos

    E o java 9? T-T

    • Alex Felipe

      Opa Cesar, blz? Vou deixar marcado no nosso backlog para escrevermos a respeito. De qualquer forma, temos conteúdo a respeito do Java 9 no blog da Caelum! Veja só

      []s

  • Murillo Goulart

    Parabéns pelo post. Exemplo simples, mas muito objetivo.

    • Alex Felipe

      Opa Murillo, blz? Obrigado pelo feedback! Fico contente que tenha gostado.

      []s

  • Felipe

    Muito bom, uma prática necessária em nosso dia a dia.

    • Alex Felipe

      Obrigado pelo feedback Felipe! Com certeza a refatoração é uma prática bem valiosa durante o dia a dia de qualquer desenvolvedor, eu, por exemeplo, estou sempre pensando em como posso melhorar o código para evitar situações não desejadas durante a manutenção do código.

      []s

  • Leandro Takeda

    Muito legal Alex!

    • Alex Felipe

      Opa Leandro, fico contente que tenha gostado!

      []s

  • Pingback: Lendo e processando informações do teclado usando Java - Blog da Alura()

Próximo ArtigoProtegendo seu servidor com IPTables