Refatoração de Código: Introduzir Criação Polimórfica com Factory Method

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: Introduzir Criação Polimórfica com Factory Method

Refatoração de Código: Introduzir Criação Polimórfica com Factory Method

Vinícius Thiengo
(1329)
Go-ahead
"Esse tem sido um dos meus mantras: foco e simplicidade. Simples pode ser mais difícil do que o complexo. Você tem que trabalhar duro para conseguir o seu pensamento limpo para torná-lo simples. Mas vale a pena no final, porque uma vez que você chegue lá, você pode mover montanhas."
Steve Jobs
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
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 continuamos com a série sobre métodos para Refatoração de Código, dessa vez abordando o método Introduzir Criação Polimórfica com Factory Method. Método que tem como objetivo reduzir ainda mais o número de código duplicado.

Antes de prosseguir lembro que, apesar da categoria dos artigos dos métodos de refatoração dessa série, Android, eles são válidos para qualquer projeto de software, principalmente para aquelas que têm o paradigma orientado a objetos como sendo o paradigma sendo utilizado.

Tópicos presentes no artigo:

Motivação

O método de refatoração proposto aqui é útil quando temos uma hierarquia de classes onde as subclasses dessa hierarquia implementam um ou mais métodos muito similares onde somente um passo dentro desse método é único para cada subclasse, passo esse que cria um objeto especifico para trabalhar naquele método, naquela subclasse. Muitas vezes temos vários desses métodos, onde apenas um ou a minoria dos passos são únicos quando comparando as implementações entre as subclasses.

O Factory Method faz com que o passo único (aquele trecho de criação de objeto) dentro das implementações desse método comum nas subclasses, esse passo se torne um método de conteúdo especifico dentro de cada subclasse.

Logo depois de definido esse novo método podemos mover o método comum, que agora invoca o novo método (que por sinal tem a mesma assinatura em todas as subclasses), para a superclasse, removendo assim esse trecho de código duplicado.

Note que esse método de refatoração também pode ser aplicado quando o método similar envolve apenas superclasse e subclasse. Caso onde somente um passo de criação em um método comum a subclasse e superclasse não permite que o método fique somente na superclasse.

Esse caso de subclasse e superclasse é ainda mais crítico, pois pode ocorrer de a superclasse não ter ainda nada referente ao “futuro” método comum. Isso, pois somente há uma subclasse na hierarquia. Mesmo sabendo que esse método na subclasse poderá ser fonte de códigos duplicados com a evolução da hierarquia. Nesse contexto não utilize ainda a superclasse para ser a proprietária direta do método, deixe que mais subclasses dela surjam e então o problema de código repetido aparecerá. Ai sim aplique o método de refatoração proposto aqui.

Algo importante a se notar no parágrafo anterior: Não antecipe os padrões a serem utilizados. Isso pode tomar muito tempo no desenvolvimento do software. Na maior parte das vezes você ficará seguro com o conhecimento de padrões porque está os aplicando em códigos já existentes, melhorando esses códigos. Essa prática vai lhe proporcionar o verdadeiro conhecimento de padrões de software. E qual o efeito disso? Simples. Quando estiver escrevendo códigos novos, até mesmo novos softwares, a oportunidade de utilizar o padrão XYZ para aquele trecho de código surgirá naturalmente, pois você conhece o padrão e sabe que naquele trecho em específico a aplicação dele trará um benefício maior.

Resumo: com a prática, a aplicação de padrões sem antecipá-los na definição do projeto, aparecerá naturalmente.

Código de exemplo

O exemplo de código utilizado aqui é um modelo adaptado do exemplo do método de refatoração Introduzir Criação Polimórfica com Factory Method apresentado também por Joshua Kerievsky em Refatoração Para Padrões (2005) onde o autor utiliza Test Driven Development (TDD) para desenvolvimento de parte um software. Nesse algoritmo há duas classes de uma mesma hierarquia onde classes de testes e métodos de testes foram criados para testá-las.

O grande problema é que muitos desses métodos das classes de testes somente se diferenciavam devido a um único passo onde era criada apenas uma instância de uma dessas classes que seriam testadas.

As subclasses são XMLBuilder e DOMBuilder, ambas herdam de OutputBuilder. Nosso método Ide refatoração proposto aqui será aplicado as classes de testes dessas duas subclasses de OutputBuilder. Segue o código de início, de DOMBuilderTest:

public class DOMBuilderTest extends TextCase {
private OutputBuilder builder;

public void testAddAboveRoot() {
String resultadoInvalido =
"<compras>"+
"<compra>"+
"</compra>"+
"</compras>"+
"<cliente>"+
"</cliente>";

builder = new DOMBuilder("compras"); /* era new XMLBuilder("compras") */
builder.addBelow("compra");

try{
builder.addAbove("cliente");
fail("java.lang.RuntimeException");
}
catch(RuntimeException ignorada){}
}
}

 

Note que o passo de instanciação, new DOMBuilder("compras"), é o único passo distinto nesse método, esse que tem uma versão similar presente na classe XMLBuilderTest. Note também que há outros métodos nas classes de testes, mas vamos seguir o exemplo somente com o método testAddAboveRoot() sendo refatorado junto as suas classes.

Mecânica

Nosso primeiro passo é extrair nossa lógica de instanciação do método testAddAboveRoot() e assim criar um novo método com um nome genérico que poderá ser utilizado também pelas outras subclasses para testes, logo nosso código agora fica:

public class DOMBuilderTest extends TextCase {
private OutputBuilder builder;

private OutputBuilder criarBuilder( String nomeRoot ){
return new DOMBuilder( nomeRoot );
}

public void testAddAboveRoot() {
String resultadoInvalido =
"<compras>"+
"<compra>"+
"</compra>"+
"</compras>"+
"<cliente>"+
"</cliente>";

builder = criarBuilder( "compras" );
builder.addBelow("compra");

try{
builder.addAbove("cliente");
fail("java.lang.RuntimeException");
}
catch(RuntimeException ignorada){}
}
}

 

Importante notar que o tipo de retorno de nosso novo método, criarBuilder(), é o tipo da superclasse da hierarquia de subclasses, DOMBuilder e XMLBuilder, que estão sendo utilizadas pelas classes de testes. Isso para que todas as classes de testes possam ter exatamente o mesmo método sendo implementado e não seja mais necessário código especifico para o método testAddAboveRoot() em cada uma dessas classes.

Nosso segundo passo é aplicar a mesma estratégia de extrair método nas subclasses irmãs de DOMBuilderTest. Em nosso caso temos apenas XMLBuilderTest:

public class XMLBuilderTest extends TestCase {
private OutputBuilder builder;

private OutputBuilder criarBuilder( String nomeRoot ){
return new XMLBuilder( nomeRoot );
}

public void testAddAboveRoot() {
String resultadoInvalido =
"<compras>"+
"<compra>"+
"</compra>"+
"</compras>"+
"<cliente>"+
"</cliente>";

builder = criarBuilder( "compras" );
builder.addBelow("compra");

try{
builder.addAbove("cliente");
fail("java.lang.RuntimeException");
}
catch(RuntimeException ignorada){}
}
}

 

Note que somente o corpo do método criarBuilder() é que mudou, porém a assinatura é a mesma que a de DOMBuilderTest.

O terceiro passo é modificar a superclasse de DOMBuilderTest e XMLBuilderTest para que ela receba além de outras entidades o código do método testAddAboveRoot(). Porém como informado no inicio do exemplo, o TDD está sendo utilizado para desenvolvimento desse algoritmo em Java. Esse script utiliza códigos do framework de testes, JUnit. Não é jogo para nós modificarmos a superclasse TestCase que é parte desse framework, logo vamos criar uma nova superclasse onde ela herdará de TestCase e as subclasses DOMBuilderTest e XMLBuilderTest herdarão dela. Nossa nova classe será a AbstractBuilderTest:

public class AbstractBuilderTest extends TestCase {
...
}

public class XMLBuilderTest extends AbstractBuilderTest {
...
}

public class DOMBuilderTest extends AbstractBuilderTest {
...
}

 

Nosso quarto passo é mover as entidades das subclasses de testes que são necessárias em nossa nova superclasse. Movendo a variável de instância builder e o método testAddAboveRoot() para a superclasse, temos:

public abstract class AbstractBuilderTest extends TestCase {
private OutputBuilder builder;

protected abstract OutputBuilder criarBuilder( String nomeRoot );

public void testAddAboveRoot() {
String resultadoInvalido =
"<compras>"+
"<compra>"+
"</compra>"+
"</compras>"+
"<cliente>"+
"</cliente>";

builder = criarBuilder( "compras" );
builder.addBelow("compra");

try{
builder.addAbove("cliente");
fail("java.lang.RuntimeException");
}
catch(RuntimeException ignorada){}
}
}

 

Note que além de termos transformado nossa superclasse em uma abstract class também colocamos uma assinatura abstrata para o método criarBuilder() (agora com o tipo de acesso protected). Isso para que seja possível a utilização desse método dentro do método testAddAboveRoot() e ao mesmo tempo para que as subclasses o implemente.

Note que a classe não necessita de ser abstract. Em nosso caso foi necessário devido a assinatura de um método abstrato.

A partir desse quarto passo nossas classes DOMBuilderTest e XMLBuilderTest não mais têm o método testAddAboveRoot() e a variável de instância builder. Logo ambas agora estão implementando o padrão Factory Method.

Curiosidade

O método que foi movido para a superclasse para evitar código repetido é a implementação do padrão de projeto Template Method.

Note que ainda temos outros dois passos. No quinto passo temos de aplicar as mesmas técnicas dos passos um a quatro para outros métodos similares (além de testAddAboveRoot()) entre as subclasses DOMBuilderTest e XMLBuilderTest, isso seguindo nosso código exemplo.

Nosso sexto e último passo é verificar e fornecer (se necessário) uma implementação padrão para nosso método criarBuilder(), na superclasse. Isso é comum quando temos muitas subclasses onde em algumas o método criarBuilder() estaria instanciando exatamente o mesmo tipo de objeto, logo para evitar ainda mais repetições teríamos essa implementação comum, removendo então a palavra chave abstract. Em nosso código exemplo não há necessidade.

Algo que pode parecer negativo, mas na verdade é um passo para o ganho de ter o Factory Method implementado em nossos projetos é: quando temos instâncias que seriam criadas em nossas subclasses de testes onde algumas dessas instâncias necessitassem de alguns parâmetros específicos somente a elas.

Devido a necessidade de uma assinatura comum em nosso método que seria implementado nas subclasses de testes, teríamos de colocar esse método comum tendo assinatura com parâmetros que são desnecessários em algumas subclasses, deixando assim o código um pouco confuso para outros programadores. Tendo em mente que para a geração de algumas instâncias, alguns parâmetros não teriam alguma utilidade.

Para ficar mais claro o problema dos parâmetros específicos, se no exemplo de código desse artigo nossa classe DOMBuilder necessitasse de ter também um inteiro na instanciação (new DOMBuilder( String, int )) e nossa classe XMLBuilder continuasse apenas com o parâmetro do tipo String (new XMLBuilder( String )) nosso método Factory Method deveria ter a assinatura com ambos os parâmetros: criarBuilder( String, int ).

Conclusão

Aplicando o método de refatoração Introduzir Criação Polimórfica com Factory Method conseguimos diminuir ainda mais o número de códigos duplicados, por consequência temos um código ainda mais limpo.

Mesmo que não seja possível ter um método comum com a mesma assinatura para a criação das instancias, mesmo nesse caso pode ser melhor persistir com a implementarão do Factory Method por meio do método de refatoração proposto aqui, isso, pois os ganhos podem ser bem superiores ao problema de pouca perda de leitura de código devido a esse detalhe de parâmetros extras.

Note que apesar do método de refatoração Introduzir Criação Polimórfica com Factory ser muito similar ao método de refatoração Substituir Construtores Por Métodos de Criação, esse são aplicados a contexto distintos.

Outros artigos da série

Segue abaixo todos os métodos de refatoração já apresentados dessa série de 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

Mover Acumulação Para Parâmetro Coletor

Compor Method

Formar Template Method

Substituir Lógica Condicional Por Strategy

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
O Codificador Limpo - Um código de conduto para programadores profissionaisO Codificador Limpo - Um código de conduto para programadores profissionaisLivros
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
Persistência Com Firebase Android - Parte 1Persistência Com Firebase Android - Parte 1Android

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