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

Investir em Você é Barra de Ouro a R$ 2,00. Cadastre-se e receba grátis conteúdos Android sem precedentes! Você receberá um email de confirmação. Somente depois de confirma-lo é que eu poderei lhe enviar os conteúdos semanais exclusivos. Os artigos em PDF são entregues somente para os inscritos na lista.

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
(1608)
Go-ahead
"O método consciente de tentativa e erro é mais bem-sucedido que o planejamento de um gênio isolado."
Peter Skillman
Prototipagem Android
Capa do curso Prototipagem Profissional de Aplicativos
TítuloAndroid: Prototipagem Profissional de Aplicativos
CategoriasAndroid, Design, Protótipo
AutorVinícius Thiengo
Vídeo aulas186
Tempo15 horas
ExercíciosSim
CertificadoSim
Acessar Curso
Quer aprender a programar para Android? Acesse abaixo o curso gratuito no Blog.
Lendo
TítuloManual de DevOps: como obter agilidade, confiabilidade e segurança em organizações tecnológicas
CategoriaEngenharia de Software
Autor(es)Gene Kim, Jez Humble, John Willis, Patrick Debois
EditoraAlta Books
Edição1ª
Ano2018
Páginas464
Conteúdo Exclusivo
Investir em Você é Barra de Ouro a R$ 2,00. Cadastre-se e receba gratuitamente conteúdos Android sem precedentes!
Email inválido

Tudo bem?

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

Desta 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 na vídeo aula do artigo:

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).

Antes de prosseguir, não esqueça de se inscrever na ðŸ“« lista de e-mails do Blog para receber em primeira mão todos os conteúdos exclusivos sobre desenvolvimento e codificação limpa.

Abaixo os tópicos que estaremos abordando em 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.

Então é isso.

Por fim, não deixe de se inscrever na 📩 lista de e-mails do Blog para receber os conteúdos de desenvolvimento e codificação limpa exclusivos, em primeira mão e também...

... na versão em PDF (versão liberada somente para os inscritos da lista de e-mails).

Abraço.

Outros artigos da série

Você está estudando séries, aulas, sobre refatoração de código?

Abaixo a lista de todos os artigos já liberados desta série do Blog sobre refatoração de código:

Fonte

Refatoração para Padrões

Investir em Você é Barra de Ouro a R$ 2,00. Cadastre-se e receba grátis conteúdos Android sem precedentes!
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...