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

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

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

Vinícius Thiengo
(1933)
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 vamos dar continuidade com a série sobre Refatoração de Código. Desta 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:

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.

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

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

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

Abaixo a lista de artigos já liberados desta série do Blog sobre "Refatoração de Código":

Fontes

Refatoração para Padrões

Use a Cabeça! Padrões de Projetos

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

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