Refatoração de Código: Substituir Construtores Por Métodos de Criação

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: Substituir Construtores Por Métodos de Criação

Refatoração de Código: Substituir Construtores Por Métodos de Criação

Vinícius Thiengo
(2056) (2)
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 post será apresentado um método utilizado para refatoração de código, mais precisamente o método Substituir Construtores por Métodos de Criação.

O objetivo desse método é apresentar no código a intenção da criação de uma instância com determinados tipos e quantidades de parâmetros, tendo em mente que ele contribui para a substituição da família de construtores sobrecarregados que podem estar presentes em classes do domínio do problema.

Tópicos presentes no artigo:

Motivação

A real problemática na presença de família de construtores é que para o programador que desenvolveu a classe (ao menos na época em que ele desenvolveu a classe) saber o porquê da utilização de vários construtores e quais tipos de comportamentos terá o objeto criado é algo simples para ele, até porque foi quem desenvolveu o algoritmo.

Esse tipo de problema está presente em classes que não convém a criação de subclasses, pois além de evitar ainda mais classes no domínio do problema tem que a diferença entre os tipos de instâncias não existe devido aos diferentes atributos sendo utilizados e sim aos comportamentos de alguns métodos de acordo com os valores dos atributos.

O que? Não entendi nada.

Você provavelmente já deve ter ouvido falar que se uma classe apresenta mais de um comportamento ou mais de uma tarefa principal, essa classe na verdade deveria ter subclasses ou novas classes irmãs para que esses comportamentos sejam corretamente divididos.

Sim, eu concordo com isso também. Mas algumas vezes é cabível optar por deixar os métodos (comportamentos) na mesma classe, pois assim você não infla seu projeto com classes que somente têm um único ou poucos métodos.

Um exemplo simples. Um projeto de um jogo medieval tem uma classe Espada que tem, entre vários métodos, dois métodos que nunca podem ser utilizados ao mesmo tempo, digo, não há configuração de estado de objeto (valores dos atributos) que permita que os métodos seguintes, corteTrovao() e corteFogo(), possam ser utilizados, ou seja, é possível utilizar um ou o outro de acordo com os valores dos atributos.

Caso você escolhesse por separar subclasses somente por causa desses dois métodos seu projeto teria ao menos três classes para representar a classe Espada, seriam elas: Espada, EspadaTrovao e EspadaFogo. Nesse caso acredito não ser uma boa escolha.

Algumas vezes é necessário, na classe, a utilização da mesma assinatura de construtor para a obtenção da instância trabalhando de maneira diferente de acordo com os valores dos atributos, esse caso é ainda mais problemático, pois não é possível (ao menos na maioria das linguagens, se não todas) a criação de construtores com exatamente a mesma assinatura.

O método de refatoração desse artigo vai nos auxiliar no código de exemplo logo abaixo. Mais precisamente, vai nos auxiliar em como colocar intenção na criação de instâncias em classes onde não é viável a solução que implica na divisão da tarefa em várias outras subclasses ou classes irmãs.

Código de exemplo

A seguir é apresentada a classe Emprestimo. Note que há uma família de construtores e que para programadores não envolvidos na criação da classe é difícil de entender qual tipo de comportamento terá a classe Emprestimo depois da utilização dos construtores:

public class Emprestimo {
private Estrategia estrategia;
private double obrigacao;
private double paraSaldar;
private int escalaDeRisco;
private Date vencimento;
private Date expiracao;


public Emprestimo(double obrigacao, int escalaDeRisco, Date vencimento){
this(obrigacao, 0.0, escalaDeRisco, vencimento, null);
}

public Emprestimo(double obrigacao, int escalaDeRisco, Date vencimento, Date expiracao){
this(obrigacao, 0.0, escalaDeRisco, vencimento, expiracao);
}

public Emprestimo(double obrigacao, double paraSaldar, int escalaDeRisco, Date vencimento, Date expiracao){
this(null, obrigacao, paraSaldar, escalaDeRisco, vencimento, expiracao);
}

public Emprestimo(Estrategia estrategia, double obrigacao, int escalaDeRisco, Date vencimento, Date expiracao){
this(estrategia, obrigacao, 0.0, escalaDeRisco, vencimento, expiracao);
}

public Emprestimo(Estrategia estrategia, double obrigacao, double paraSaldar, int escalaDeRisco, Date vencimento, Date expiracao){
this.estrategia = estrategia;
this.obrigacao = obrigacao;
this.paraSaldar = paraSaldar;
this.escalaDeRisco = escalaDeRisco;
this.vencimento = vencimento;
this.expiracao = expiracao;

if( estrategia == null ){
if( expiracao == null ){
this.estrategia = new EstrategiaTempoLimitado();
}
else if( vencimento == null ){
this.estrategia = new EstrategiaRecorrente();
}
else {
this.estrategia = new EstrategiaRecorrenteTempoLimitado();
}
}
}

/* TODO - METODOS CLASSE */
}

 

Note que não foram apresentados os métodos da classe, na verdade você não precisa "quebrar a cabeça" em saber o porquê do parâmetro XYZ... o que tem que ter em mente é que a classe apresentada acima permite a criação de instâncias dela com inúmeros comportamentos diferentes representando os tipos distintos de empréstimos que o sistema pode ter.

Aqui estaremos trabalhando com a possibilidade de criar somente os seguintes tipos de empréstimos: Tempo limitado, Recorrente e Recorrente com tempo limitado.

Vale lembrar que é possível sim utilizar a estratégia de criação de subclasses ou classes irmãs, mas no caso em que a utilização do método Substituir Construtores por Métodos de Criação permite uma implementação mais simples e sem repetição de código, essa tende a ser a solução mais viável.

Antes de iniciar o processo de refatoração devemos primeiro identificar o construtor que é utilizado como base por todos os outros construtores, mesmo quando de forma implícita (um segundo construtor encadeado chama o construtor base). Esse construtor completo, caso não exista, deve ser criado. Em nosso exemplo é o último construtor apresentado:

...
public Emprestimo(Estrategia estrategia, double obrigacao, double paraSaldar, int escalaDeRisco, Date vencimento, Date expiracao)
...

 

Note que se em seu caso cada construtor tenha um código interno diferente, lógica de negócio própria. Nesse caso você deve aplicar a estratégia Encadear Construtores onde todos os construtores referenciam ao construtor base, deixando toda a inicialização de atributos no construtor base. Depois desse processo de refatoração finalizado é que poderá seguir com a aplicação do método Substituir Construtores por Métodos de Criação.

Mecânica

O primeiro passo do processo de refatoração do método proposto neste artigo é descobrir ao menos um código cliente que utiliza algum dos construtores da classe Emprestimo. No caso vamos utilizar um trecho de um código de de uma classe de testes. Nesse trecho será criada uma instancia de Emprestimo para trabalhar como um empréstimo do tipo limitado:

...
public void testeTempoLimitadoSemPagamentos(){
Emprestimo emprestimo = new Emprestimo( obrigacao, escalaDeRisco, vencimento );
}
...

 

Logo depois vamos aplicar os métodos de refatoração Extrair Método e Mover Método, onde respectivamente criaremos um método que tem como código interno o retorno de uma instancia que é criada no mesmo molde (atributos com os mesmo valores) como no exemplo do código de teste acima e logo depois a definição desse método como um método publico e estático na classe Emprestimo.

Note que o método como estático é um passo opcional, porém uma boa escolha, pois não precisamos de uma instância para criar outras instâncias.

Seguindo com o código, agora temos na classe Emprestimo:

...
public static Emprestimo criarTempoLimitado( double obrigacao, int escalaDeRisco, Date vencimento ){
return( new Emprestimo( obrigacao, escalaDeRisco, vencimento ) );
}
...

 

Dessa forma o código cliente de testes ficaria da seguinte maneira:

...
public void testeTempoLimitadoSemPagamentos(){
Emprestimo emprestimo = Emprestimo.criarTempoLimitado( obrigacao, escalaDeRisco, vencimento );
}
...

 

Diferença notável: o developer que precisa utilizar a classe Emprestimo não perderá tempo estudando os construtores para saber como criar um empréstimo com estratégia de tempo limitado, já haverá um método informando sobre como obter uma instância com o comportamento desse tipo de empréstimo.

Método de refatoração Extrair Método:

Esse método de refatoração é bem simples e dispensa um artigo somente para a explicação dele.

Quando se tem um método que realiza mais de uma tarefa ou um construtor que deveria ser na verdade um método de criação, esse método de refatoração nos permiti criar outro método (ou métodos) a partir dessas condições, dividindo ainda mais o código.

Ponto positivo: os códigos encapsulados ficam menores e mais fáceis de entender.

Ponto negativo: o projeto pode acabar tendo muitos outros métodos, podendo ocasionar com isso uma perda de performance na execução, pois cada chamada de método implica em uma serie de recursos (incluindo espaço de memória) alocados somente para aquela chamada.

Método de refatoração Mover Método:

Esse método de refatoração, como o método Extrair Método, é bem simples e dispensa a necessidade de um artigo para a explicação dele.

Esse método faz exatamente o que indica o nome. Quando se cria um novo método partindo de uma outra refatoração, por exemplo, é possível que esse método esteja ainda sem classe ou na classe errada, logo movemos ele para o local correto no projeto.

Como somente estamos movendo algo que já foi criado, não há pontos positivos ou negativos.

O segundo passo é encontrar todas os códigos clientes ou internos a classe Emprestimo que criam uma instância dessa classe com a mesma assinatura de construtor para criar um empréstimo do tipo tempo limitado. Encontradas essas chamadas, substitua todas pela utilização do método de criação criarTempoLimitado().

O terceiro passo é mover o construtor base para dentro do método criarTempoLimitado():

...
public static Emprestimo criarTempoLimitado( double obrigacao, int escalaDeRisco, Date vencimento ){
return( new Emprestimo( null, obrigacao, 0.0, escalaDeRisco, vencimento, null ) );
}
...

 

Logo depois devemos destruir o construtor que era responsável pela criação da instancia de Emprestimo com comportamento de empréstimo de tempo limitado. Apagar da classe o código abaixo:

...
public Emprestimo(double obrigacao, int escalaDeRisco, Date vencimento){
this(obrigacao, 0.0, escalaDeRisco, vencimento, null);
}
...

 

O quarto passo é repetir os passos de 1 a 3 para todos os outros construtores da classe sendo refatorada.

No quinto e último passo vamos permitir a instanciação de uma class Emprestimo somente dentro da própria classe, ou seja, a utilização do new Emprestimo() será privada. Teremos de colocar o construtor base com modificador de acesso private:

...
private Emprestimo(Estrategia estrategia, double obrigacao, double paraSaldar, int escalaDeRisco, Date vencimento, Date expiracao)
...

 

Ao final da refatoração a classe Empréstimo deverá estar da seguinte forma:

public class Emprestimo {
private Estrategia estrategia;
private double obrigacao;
private double paraSaldar;
private int escalaDeRisco;
private Date vencimento;
private Date expiracao;


private Emprestimo(Estrategia estrategia, double obrigacao, double paraSaldar, int escalaDeRisco, Date vencimento, Date expiracao){
this.estrategia = estrategia;
this.obrigacao = obrigacao;
this.paraSaldar = paraSaldar;
this.escalaDeRisco = escalaDeRisco;
this.vencimento = vencimento;
this.expiracao = expiracao;

if( estrategia == null ){
if( expiracao == null ){
this.estrategia = new EstrategiaTempoLimitado();
}
else if( vencimento == null ){
this.estrategia = new EstrategiaRecorrente();
}
else {
this.estrategia = new EstrategiaRecorrenteTempoLimitado();
}
}
}

public static Emprestimo criarTempoLimitado( double obrigacao, int escalaDeRisco, Date vencimento ){
return( new Emprestimo( null, obrigacao, 0.0, escalaDeRisco, vencimento, null ) );
}

public static Emprestimo criarRecorrente( double obrigacao, int escalaDeRisco, Date expiracao ){
return( new Emprestimo( null, obrigacao, 0.0, escalaDeRisco, null, expiracao ) );
}

public static Emprestimo criarRecorrenteTempoLimitado( double obrigacao, int escalaDeRisco, Date vencimento, Date expiracao ){
return( new Emprestimo( null, obrigacao, 0.0, escalaDeRisco, vencimento, expiracao ) );
}

public static Emprestimo criar( Estrategia estrategia, double obrigacao, double paraSaldar, int escalaDeRisco, Date vencimento, Date expiracao ){
return( new Emprestimo( estrategia, obrigacao, paraSaldar, escalaDeRisco, vencimento, expiracao ) );
}

/* TODO - METODOS CLASSE */
}

 

Note que ainda deixamos um método criar() onde o cliente do código terá a opção de fornecer a estratégia de empréstimo como parâmetro de entrada. A classe Emprestimo inicial permite a criação de mais do que três comportamentos de empréstimos, mas nesse exemplo trabalhamos apenas com três, permitindo a utilização dos outros por meio do método criar().

Conclusão

Visto que há construtores (pode ser apenas dois) que não expressam o verdadeiro tipo de comportamento que será característica da instância criada, utilize métodos de criação para que até mesmo você, que criou o código, possa voltar e evoluí-lo sem gastar muito tempo tentando primeiro entendê-lo.

Note que a aplicação do método de refatoração Substituir Construtores por Métodos de Criação não restringe a não utilização de outros construtores públicos, ou seja, se a combinação de seus construtores permitir a criação de instancias com possibilidades de 50 comportamentos diferentes, por exemplo, o que pode fazer é utilizar o método de refatoração proposto aqui para a criação de métodos que retornam instancias mais comumente utilizadas nos códigos clientes da classe refatorada.

Se você estiver utilizando linguagem de programação de tipagem dinâmica (PHP, por exemplo), ainda é possível aplicar esse método de refatoração, somente o passo de encadeamento de construtores que não será necessário, alias todas as partes que envolvem mais do que apenas um construtor. No PHP não é possível ter mais do que um construtor na mesma classe.

Outros artigos da série

Abaixo listo todos os artigos já liberados 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

Introduzir Criação Polimórfica com Factory Method

Encapsular Classes Com Factory

Encadear Construtores

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

Engenharia de Software: Código Limpo na PráticaEngenharia de Software: Código Limpo na PráticaDesenvolvimento Web
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 (2)

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...
johnathan (1) (0)
07/03/2016
Olá Thiengo,voçê tem algum video falando sobre DownloadManager ou como baixar files(mp3,pdf,doc)
Responder
Vinícius Thiengo (0) (0)
07/03/2016
Fala Johnathan, blz?
Não tenho conteúdos sobre p DownloadManager. Encontrei dois links que podem lhe ajudar (assumindo que vc já tem acesso aos links da doc do Android), seguem:

http://blog.vogella.com/2011/06/14/android-downloadmanager-example/

http://www.mysamplecode.com/2012/09/android-downloadmanager-example.html

Sobre o download dos files, acredito que com o Retrofit 2.0 (http://www.thiengo.com.br/library-retrofit-2-no-android ) é possível tb realizar o download, terá de testar nesse formato. Abraço
Responder