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

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

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

Vinícius Thiengo
(2070)
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 continuamos com a série sobre métodos para Refatoração de Código.

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

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.

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.

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

Segue abaixo todos os artigos de refatoração 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
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...