Refatoração de Código: Encapsular Classes Com Factory

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: Encapsular Classes Com Factory

Refatoração de Código: Encapsular Classes Com Factory

Vinícius Thiengo
(1204)
Go-ahead
"Foque em ser produtivo ao invés de ser oculpado."
Tim Ferris
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 continuo com a série sobre Refatoração de Código, dessa vez falando sobre o método de refatoração Encapsulando Classes com Factory.

Ressaltando que as técnicas apresentadas nessa série de posts podem ser aplicadas a qualquer projeto de software que está sendo trabalhado no paradigma orientado a objetos.

Antes de prosseguir com os estudos do método proposto aqui é recomendado que você conheça os padrões Factory. Aqui no blog temos artigos sobre esses padrões. Se ainda não os conhece estude como apresentado a seguir, de cima para baixo. Segue:

Tópicos presentes no artigo:

Motivação

Você tem um pacote onde subclasses compartilham uma mesma interface (Interface estrutura ou classe), porém o código cliente mantém a obtenção de cada uma dessas subclasses de forma direta via palavra reservada new.

Entenda "compartilham uma mesma interface" como: além da implementação de métodos obrigatórios da interface em comum, também como sendo uma espécie de contrato com o código cliente que permite com que somente os métodos públicos dessa interface é que possam ser utilizados por ele, mesmo esse utilizando subclasses especificas.

Note que o informado no parágrafo anterior nos diz de forma implícita que não há motivos para permitirmos que o código cliente saiba quais são as subclasses que serão criadas, pois a interface de trabalho é a mesma. Permitir isso somente aumenta o peso conceitual do pacote das subclasses, pois qualquer alteração nelas pode ocasionar em alteração também no código cliente.

Isso sem contar que se o código cliente não estiver utilizando um supertipo para trabalhar com as subclasses, o projeto perde muito em evolução, pois nesse contexto sem supertipo os códigos das classes cliente e subclasses ficam ainda mais acoplados uns aos outros.

Utilizando o padrão Factory por meio do método Encapsular Classes com Factory podemos criar métodos de criação dessas subclasses em apenas uma classe onde esses métodos indicam a intenção da criação, além de diminuir o peso conceitual do pacote dessas subclasses e também de manter nosso código, ao menos nesse pacote, respeitando o princípio "Programe para interfaces, não para implementação" garantindo que os códigos clientes interajam com as instâncias dessas subclasses por meio de uma interface em comum, supertipo.

Resumo até aqui: esse método de refatoração é aplicado frequentemente quando há subclasses que compartilham a mesma interface além de residirem no mesmo pacote (ou domínio do problema).

Código de exemplo

O exemplo que vamos seguir utiliza um código de mapeamento objeto-relacional que é utilizado para ler e escrever objetos em um banco de dados relacional.

Assuma que a hierarquia de classes abaixo reside no mesmo pacote, descritores:

public abstract class DescritorAtributo {
protected DescritorAtributo(...)
...
}

public class DescritorBoolean extends DescritorAtributo {
public DescritorBoolean(...){
super(...);
}
...
}

public class DescritorPadrao extends DescritorAtributo {
public DescritorPadrao(...){
super(...);
}
...
}

public class DescritorReferencia extends DescritorAtributo {
public DescritorReferencia(...){
super(...);
}
...
}

Nesse exemplo vamos focar na classe DescritorPadrao, pois o processo de refatoração aplicado nessa classe poderá ser aplicado em todas as outras, que por sinal, se tratando de mapeamento com uma base relacional, temos que podem haver muitas outras subclasses.

Mecânica

Nosso primeiro passo é identificar no código cliente o tipo de instância que pode ser criada (ou mapeada) pelo construtor de DescritorPadrao. Segue um código cliente, criarDescritoresAtributo():

...
protected List criarDescritoresAtributo(){
List result = new ArrayList();
result.add( new DescritorPadrao( "id", getClass(), Integer.TYPE ) );
result.add( new DescritorPadrao( "data_criacao", getClass(), Date.class ) );
result.add( new DescritorPadrao( "data_ultima_atualizacao", getClass(), Date.class ) );
result.add( new DescritorReferencia( "criado_por", getClass(), Usuario.class, UsuarioRemoto.class ) );
result.add( new DescritorReferencia( "ultima_atualizacao_por", getClass(), Usuario.class, UsuarioRemoto.class ) );
result.add( new DescritorPadrao( "versao_otimista_bloqueio", getClass(), Integer.TYPE ) );
return( result );
}
...

O que primeiro notamos é que o construtor de DescritorPadrao no código anterior está sendo utilizado para mapear os tipos Integer e Date. O que devemos fazer é focar em uma entidade de cada vez. Vamos começar pelo tipo Integer.

Dentro da mesma classe do método criarDescritoresAtributo() (o código cliente) vamos criar um novo método de criação, estático e de acesso público. Esse método será responsável por retornar uma instancia de DescritorPadrao com mapeamento para um tipo Integer:

...
public static DescritorAtributo paraInteger(String atributo, Class classe){
return new DescritorPadrao( atributo, classe, Integer.TYPE );
}
...

 

Como queremos que os códigos clientes possam interagir com qualquer subclasse de DescritorAtributo, respeitando a interface comum, colocamos o tipo de retorno do método como sendo do tipo DescritorAtributo. Veja que o nome do método indica a instância para qual o mapeamento será criado, logo podemos encapsular o parâmetro Integer.TYPE.

Importante

Muitas vezes serão utilizados os "..." (reticências) para evitar a sobrecarga de código, pois o objetivo aqui, na série, é saber quando e como aplicar o método de refatoração ou padrão proposto nos artigos, logo não "quebre a cabeça" tentando entender o porquê de utilizarmos Integer.TYPE como argumento de uma instanciação e Date.class em outra instanciação. Ou qualquer outro detalhe dos códigos apresentados na série, eles foram omitidos para não prejudicar o entendimento.

Definido o método de criação dentro da classe de nosso código cliente, devemos agora mover esse método para nossa classe que trabalhará como classe Factory em nosso pacote descritores, a classe DescritorAtributo:

public abstract class DescritorAtributo {
...
public static DescritorAtributo paraInteger(String atributo, Class classe){
return new DescritorPadrao( atributo, classe, Integer.TYPE );
}
...
}

 

Feito isso o código cliente anterior deve ficar como:

...
protected List criarDescritoresAtributo(){
List result = new ArrayList();
result.add( DescritorPadrao.paraInteger( "id", getClass() ) );
...
result.add( DescritorPadrao.paraInteger( "versao_otimista_bloqueio", getClass() ) );
return( result );
}
...

 

O código acima além de apresentar como fica parte do código cliente anterior também é o segundo passo de nossa refatoração, onde buscamos por todas as instanciações de DescritorPadrao com mapeamento para um Integer e então as substituímos pela nova forma de criação, com a classe Factory DescritorAtributo.

O terceiro passo é aplicar no código os passos um e dois para os outros tipos de instâncias que DescritorPadrao pode mapear. Seguindo o código cliente ainda temos o tipo Date, que entraria em nossa Factory class da seguinte forma:

public abstract class DescritorAtributo {
...
public static DescritorAtributo paraInteger(String atributo, Class classe){
return new DescritorPadrao(atributo, classe, Integer.TYPE);
}

public static DescritorAtributo paraDate(String atributo, Class classe){
return new DescritorPadrao(atributo, classe, Date.class);
}
...
}

O quarto passo é declarar o construtor de DescriorPadrao como sendo protected, permitindo acesso somente a entidades do mesmo pacote removendo a possibilidade do código cliente poder instanciar essa classe diretamente.

O quinto e último passo é repetir todos os passos de um a quatro aplicados a classe DescritorPadrao em todas as outras subclasses de DescritorAtributo.

O que pode ser visto por você como um ponto negativo em aplicar esse método de refatoração e terminarmos com uma classe implementando o padrão Factory é que se houver a adição de algum outro tipo atendido por DescritorPadrao, o tipo Double, por exemplo, teremos de adicionar mais código a nossa Factory class. Realmente esse passo, quando utilizando o padrão Factory, é inevitável. Porém, se tratando de programação e evolução, não veja esse passo de “adição de código” como algo negativo, o negativo está em "alterar código”. Adição, em projeto com código limpo ou não, será sempre um passo necessário.

Conclusão

O método de refatoração Encapsular Classes Com Factory tem como objetivo restringir acesso a algoritmos de criação, incluindo acesso a classes específicas. Como consequência diminui o peso conceitual do pacote dessas subclasses.

O Factory que será implementado depende do problema de código encontrado, como informado nos artigos dos factories: não há o melhor entre eles para qualquer problema que necessite de um Factory, há na verdade o melhor para o problema descoberto.

No projeto de exemplo desse artigo utilizamos o Simples Factory. Pode acreditar, mesmo sendo algo similar ao Abstract Factory, devido a família de classes envolvidas.

No artigo Engenharia de Software: Código Limpo na Prática também é implementado o padrão Factory, porém o caminho adotado foi um pouco diferente. Conseguimos no exemplo desse post abstrair qual classe de vídeo é criada, deixando o código cliente apenas ciente da utilização dos métodos da interface comum entre as subclasses.

Outros artigos da série

Abaixo há a lista de artigos já liberados da série 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

Introduzir Criação Polimórfica com Factory Method

Encadear Construtores

Substituir Construtores Por Métodos de Criação

Fontes

Refatoração para Padrões

Use a Cabeça! Padrões de Projetos

Vlw.

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

Relacionado

KISS - Mantenha Isso Estupidamente SimplesKISS - Mantenha Isso Estupidamente SimplesAndroid
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

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