Refatoração de Código: Mover Acumulação Para Parâmetro Coletor

Receba em primeira mão, e com prioridade, os conteúdos Android exclusivos do Blog. Você receberá um email de confirmação. Somente depois de confirma-lo é que poderei lhe enviar os conteúdos exclusivos.

Email inválido.
Blog /Android /Refatoração de Código: Mover Acumulação Para Parâmetro Coletor

Refatoração de Código: Mover Acumulação Para Parâmetro Coletor

Vinícius Thiengo
(988)
Go-ahead
"Descobri que pequenas vitórias, pequenos projetos, pequenas diferenças muitas vezes fazem grandes diferenças."
Rosabeth Moss Kanter
Kotlin Android
Capa do livro Desenvolvedor Kotlin Android - Bibliotecas para o dia a dia
TítuloDesenvolvedor Kotlin Android - Bibliotecas para o dia a dia
CategoriasAndroid, Kotlin
AutorVinícius Thiengo
Edição
Capítulos19
Páginas1035
Acessar Livro
Treinamento Oficial
Android: Prototipagem Profissional de Aplicativos
CursoAndroid: Prototipagem Profissional de Aplicativos
CategoriaAndroid
InstrutorVinícius Thiengo
NívelTodos os níveis
Vídeo aulas186
PlataformaUdemy
Acessar Curso
Receitas Android
Capa do livro Receitas Para Desenvolvedores Android
TítuloReceitas Para Desenvolvedores Android
CategoriaDesenvolvimento Android
AutorVinícius Thiengo
Edição
Ano2017
Capítulos20
Páginas936
Acessar Livro
Código Limpo
Capa do livro Refatorando Para Programas Limpos
TítuloRefatorando Para Programas Limpos
CategoriaEngenharia de Software
AutorVinícius Thiengo
Edição
Ano2017
Capítulos46
Páginas599
Acessar Livro
Quer aprender a programar para Android? Acesse abaixo o curso gratuito no Blog.
Conteúdo Exclusivo
Receba em primeira mão, e com prioridade, os conteúdos Android exclusivos do Blog.
Email inválido

Opa, blz?

Nesse artigo damos continuidade a série de conteúdos sobre Refatoração de Código, com objetivo de obtermos códigos mais eficientes.

Dessa vez abordaremos o método de refatoração Mover Acumulação Para Parâmetro Coletor, método que já utilizei em um vídeo da série Firebase no Android, mais precisamente no vídeo do post: Atualização de Dados, Firebase Android - Parte 3

Já lhe adianto que esse é também um método bem simples de ser aplicado, digo "também" nessa série já apresentei outros métodos ao menos tão simples quanto o proposto aqui.

Lembrando que os métodos de refatoração dessa série podem ser utilizados em algoritmos dos mais diversos, principalmente aqueles projetos de software que trabalham no paradigma orientado a objetos (alguns dos métodos dessa série somente são úteis nesse paradigma).

Tópicos presentes no artigo:

Um pouco de história

O método de refatoração Mover Acumulação Para Parâmetro Coletor foi criado por Kent Beck. É esse camarada mesmo que você está pensando, o mesmo do JUnit, eXtreme Programming, Padrões de Implementação, Test Driven Development (TDD), …

Uma outra característica marcante de Kent Beck é que ele criou alguns padrões, ou técnicas de código limpo, que são extremamente simples e úteis para a melhora do código. Por exemplo, foi ele quem criou o padrão Cláusula de Guarda, o padrão mais simples, e não menos importante, nessa série de métodos para refatoração de código.

Motivação

Há métodos que têm várias chamadas a outros métodos ou até mesmo chamadas a trechos de códigos dentro dele, trechos que acumulam resultados em uma única variável local.

As problemáticas nessa abordagem são das mais variadas, as principais ficam em:

  • Código que informa pouco a tarefa dele, não intuitivo. Isso quando há vários trechos de código juntamente a chamadas de métodos como sendo o algoritmo;
  • A possível criação de várias instâncias para poderem acumular os resultados dos trechos de códigos ou métodos, algo comum em recursões com apenas parâmetros de entrada.

O método proposto aqui trabalha com o objetivo de minimizar as problemáticas citadas anteriormente, além de otimizar parte do código, trabalhando com apenas uma única instância que será a coletora de dados.

Vale ressaltar que a parte sobre "otimização" não descarta a utilização de um medidor de desempenho em seu código assim que ele apresentar problemas na execução, digo, lentidão. O medidor de desempenho sempre é uma forma segura de descobrir os reais gargalos do sistema.

Vamos seguir com o código de exemplo para facilitar o entendimento dessa refatoração.

Código de exemplo

Abaixo o código que estaremos utilizando como exemplo, na verdade é um trecho de um código (método toString()) de uma classe que trabalha também o parse XML. Segue:

class TagNode{
...
public String toString(){

String resultado = new String();
resultado += "<" + nomeTag + " " + atributos + ">";

Iterator it = filhos.iterator();
while( it.hasNext() ){
TagNode nodo = (TagNode) it.next();
resultado += nodo.toString();
}

if( !valorTag.equals("") ){
resultado += valorTag;
}

resultado += "</" + nomeTag + ">";
return( resultado );
}
}

Note a acumulação na variável local resultado e também trechos de código que poderiam se tornar métodos, consequentemente passando a qualquer developer a intenção dos métodos principal e auxiliares, com isso evitando a necessidade de comentários para que o código seja entendido corretamente.

Você está falando de comentários em código como se fossem algo negativo. É isso mesmo?

Na verdade sim. O que pode parecer apenas uma opinião. O grande problema dos comentários não é que eles colocam ainda mais linhas no projeto, o problema está na não evolução deles juntamente ao código. Ou seja, muitas vezes eles existem para facilitar o entendimento de um trecho de código não trivial. Esse trecho evolui e é comum a prática de não evoluirem ou removerem o comentário, ele continua o mesmo, porém agora sem significado para o código atualizado. A consequência disso é a confusão criada para outros developers do projeto, aqueles que realmente leem os comentários.

Logo utilizar técnicas simples de código limpo, como, nomear classes, métodos e variáveis de forma intuitiva e ter cada classe e método com tarefas únicas. Esses simples passos podem ser mais que o suficiente para evitar comentários.

Mecânica

Note que no exemplo de código anterior, com uma variável local sendo utilizada para acumulação, na verdade o que está ocorrendo é a criação de uma nova instância para cada concatenação, isso porque o Java trabalha dessa maneira: cria uma nova instância do tipo String para cada modificação na variável String.

Nosso primeiro passo é identificar um método que tem código de acumulação em uma variável local, já temos esse método identificado, o toString(). Ainda no primeiro passo devemos fazer com que essa variável local seja única na acumulação durante os trechos de códigos de nosso método, caso o tipo atual dessa variável não permita isso, altere esse tipo.

No código de exemplo o tipo String, como informado no início dessa seção, não realiza a acumulação de maneira eficiente, pois cada concatenação implica em uma nova instância de String na memória. Sabendo disso, vamos passar a utilizar o tipo StringBuffer. Prosseguindo, temos também que alterar as acumulações que trabalham concatenação utilizando o operador “+”, alterar para chamadas ao método append() de StringBuffer. Segue código atualizado:

class TagNode{
...
public String toString(){

StringBuffer resultado = new StringBuffer("");
resultado.append( "<" + nomeTag + " " + atributos + ">" );

Iterator it = filhos.iterator();
while( it.hasNext() ){
TagNode nodo = (TagNode) it.next();
resultado.append( nodo.toString() );
}

if( !valorTag.equals("") ){
resultado.append( valorTag );
}

resultado.append( "</" + nomeTag + ">" );
return( resultado.toString() );
}
}

Nosso segundo passo é identificar um trecho de código realizando a tarefa de acumulação e então extrair esse trecho na forma de um método privado e com o tipo de retorno void, tendo em mente que nosso parâmetro coletor será o parâmetro de saída, ou seja, no projeto de exemplo abordado aqui, o resultado desse novo método deve ser vinculado a nossa variável resultado.

O primeiro trecho de acumulação encontrado é o trecho que cria uma tag de abertura em nosso parse XML. Atualizando o código com a extração de um novo método, temos:

class TagNode{
...
public String toString(){

StringBuffer resultado = new StringBuffer("");
escreveTagDeAberturaPara( resultado );

Iterator it = filhos.iterator();
while( it.hasNext() ){
TagNode nodo = (TagNode) it.next();
resultado.append( nodo.toString() );
}

if( !valorTag.equals("") ){
resultado.append( valorTag );
}

resultado.append( "</" + nomeTag + ">" );
return( resultado.toString() );
}

private void escreveTagDeAberturaPara( StringBuffer resultado ){
resultado.append( "<" );
resultado.append( nomeTag );
resultado.append( " " );
resultado.append( atributos );
resultado.append( ">" );
}
}

Nosso terceiro e último passo do método proposto, nada mais nada menos implica que devemos persistir com o passo dois para cada trecho de código que acumula dados em nossa variável coletora, resultado. Cada método aceitando ela como parâmetro e consequentemente colocando nela os dados que seriam utilizados como retorno.

Então vamos seguir aplicando o passo dois. Porém nosso próximo trecho de código acumulativo está em um laço while que deixa evidente que nosso método toString() em TagNode é recursivo.

Por que recursivo?

Porque as tags filhas da tag atual também são do tipo TagNode e dentro desse while que percorre as tags filhas temos a seguinte linha:

...
resultado.append( nodo.toString() );
...

Essa linha se torna um problema se simplesmente extrairmos esse trecho com o laço while para um método auxiliar, pois não podemos simplesmente acrescentar um parâmetro a toString(), esse é um método que está sendo sobrescrito, estaríamos criando um novo método principal, digo, alterando a assinatura dele, algo que não é nosso objetivo, além de que estaríamos, a cada chamada a nosso novo método toString(), criando um novo StringBuffer, acabando com a eficiência do parâmetro coletor.

Solução?! Vamos separar os escopos dentro de nosso método toString(), mais precisamente vamos colocar as chamadas aos trechos de códigos acumulativos em um método auxiliar que poderá ser chamado de forma recursiva sem que seja necessária novamente a chamada ao toString() de TagNode. Segue código atualizado:

class TagNode{
...
public String toString(){
StringBuffer resultado = new StringBuffer("");
appendConteudosPara( resultado );
return( resultado.toString() );
}

private void appendConteudosPara( StringBuffer resultado ){
escreveTagDeAberturaPara( resultado );
escreveFilhosPara( resultado );
escreveValorPara( resultado );
escreveTagDeFechamentoPara( resultado );
}

private void escreveTagDeAberturaPara( StringBuffer resultado ){
resultado.append( "<" );
resultado.append( nomeTag );
resultado.append( " " );
resultado.append( atributos );
resultado.append( ">" );
}

private void escreveFilhosPara( StringBuffer resultado ){
Iterator it = filhos.iterator();
while( it.hasNext() ){
TagNode nodo = (TagNode) it.next();
nodo.appendConteudosPara( resultado );
}
}

private void escreveValorPara( StringBuffer resultado ){
if( !valorTag.equals("") ){
resultado.append( valorTag );
}
}

private void escreveTagDeFechamentoPara( StringBuffer resultado ){
resultado.append( "</" );
resultado.append( nomeTag );
resultado.append( ">" );
}
}

Nosso novo método auxiliar appendConteudosPara(), além de receber nosso parâmetro coletor tem as chamadas a todos os outros métodos que acumulam dados nesse parâmetro. Logo em nosso outro novo método, escreveFilhosPara(), temos a chamada a nodo.appendConteudosPara() aplicando a recursão corretamente para atender a nosso método de refatoração.

Note que já adiantamos o "passo três" e já colocamos os novos métodos escreveValorPara() e escreveTagDeFechamentoPara() no código.

Nosso novo método toString() não é recursivo (apesar de ter trecho com recursão), utiliza somente uma instância para acumular os dados e retorna o resultado como deveria, isso tudo de maneira mais eficiente que o script antigo desse método, e ainda passando a intenção do código com os métodos auxiliares tendo nomes auto comentados.

Curiosidade

Aplicando os passos dois e três estamos aplicando o método de refatoração Compor Method.

Conclusão

Simples e sem necessidade de conhecimento prévio de algum padrão de projeto. O método de refatoração Mover Acumulação Para Parâmetro Coletor somente traz benefícios ao seu projeto de software. Primeiro que ele deixa o código mais intuitivo e segundo que tende a colocar em cada método somente uma tarefa, algo altamente recomendo em projetos orientados a objetos.

Como com o método Compor Method, a aplicação do método proposto aqui é ainda mais indicativa, digo, fácil de perceber quando necessária. Basta alguns trechos de código “embolados" para partir para a refatoração.

Outros artigos da série

Estudando  série sobre refatoração de código? Abaixo a lista de todos os artigos já liberados dessa série sobre refatoração de código:

Internalizar Singleton

Mover Embelezamento Para Decorator

Substituir Condicionais que Alteram Estado por State

Introduzir Objeto Nulo

Unificar Interfaces Com Adapter

Extrair Adapter

Mover Conhecimento de Criação Para Factory

Substituir Notificações Hard-Coded Por Observer

Substituir Código de Tipo Por Classe

Extrair Parâmetro

Unificar Interfaces

Limitar Instanciação Com Singleton

Compor Method

Formar Template Method

Substituir Lógica Condicional Por Strategy

Introduzir Criação Polimórfica com Factory Method

Encapsular Classes Com Factory

Encadear Construtores

Substituir Construtores Por Métodos de Criação

Fontes

Refatoração para Padrões

Vlw.

Receba em primeira mão, e com prioridade, os conteúdos Android exclusivos do Blog.
Email inválido

Relacionado

Código Limpo - Habilidades Práticas do Agile SoftwareCódigo Limpo - Habilidades Práticas do Agile SoftwareLivros
Padrões de Implementação - Um Catálogo de Padrões Indispensável Para o Dia a Dia do ProgramadorPadrões de Implementação - Um Catálogo de Padrões Indispensável Para o Dia a Dia do ProgramadorLivros
Refatoração Para PadrõesRefatoração Para PadrõesLivros
Facebook Login, Firebase Android - Parte 5Facebook Login, Firebase Android - Parte 5Android

Compartilhar

Comentários Facebook

Comentários Blog

Para código / script, coloque entre [code] e [/code] para receber marcação especifica.
Forneça seu nome válido.
Forneça seu email válido.
Forneça o comentário.
Enviando, aguarde...