Receba em primeira mão o conteúdo exclusivo do Blog, além de promoções de livros e cursos de programação. Você receberá um email de confirmação. Somente depois de confirmar é que poderei lhe enviar o conteúdo exclusivo por email.

Email inválido.
Blog /Android /Refatoração de Código: Limitar Instanciação Com Singleton

Refatoração de Código: Limitar Instanciação Com Singleton

Vinícius Thiengo12/05/2016, Quinta-feira, às 02h
(418) (11)
Go-ahead
"Construa uma voz e uma opinião em primeiro lugar e, em seguida, se essas ressoam com o público, então você vai ter uma audiência."
Adam Carolla
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áginas598
Comprar Livro
Conteúdo Exclusivo
Receba em primeira mão o conteúdo exclusivo do Blog, além de promoções de livros e cursos de programação.
Email inválido

Opa, blz?

Nesse artigo continuamos com a série Refatoração de Código para desenvolvimento de softwares com maior desempenho. Dessa vez vamos falar sobre o velho e conhecido padrão de projeto Singleton, mais precisamente do método de refatoração Limitar Instanciação com Singleton.

Esse método de refatoração vai nos ajudar a colocar o Singleton em nossos algoritmos quando enxergada a oportunidade de melhorar o código com esse padrão.

Lembrando que todos os artigos dessa série, incluindo os de padrões de projeto, podem ser utilizados para estudos e melhorias de qualquer projeto de software orientado a objetos.

Antes de prosseguir com conteúdo do método proposto aqui, é importante que você saiba o que é e como utilizar o padrão de projeto Singleton além de conhecer também o método de refatoração Substituir Construtores Por Métodos de Criação. Abaixo os links dos artigos daqui do Blog sobre esses assuntos:

Tópicos presentes no artigo:

Motivação

O código está apresentando problemas de desempenho, um medidor de desempenho informa sobre um gargalo na criação de várias instâncias.

Debugando o código você percebe que uma classe que não nunca tem o estado alterado durante a execução está sendo instanciada várias vezes, onde apenas uma instância dela, em todo o código, seria o suficiente. Obviamente que pode haver ainda outros problemas, mas esse é evidente, ao menos, um consumo desnecessário de memória. Assim você decide implementar o Singleton nessa classe.

Código de exemplo

O script de exemplo é parte de um sistema de permissões de acesso. Esse sistema tem até seis classes de permissão e todas elas têm a mesma interface e também herdam da mesma superclasse e, o principal (o que realmente indica uma refatoração para um Singleton), elas não têm mudança de estado e são instanciadas frequentemente no código.

Vamos seguir a refatoração utilizando uma classe desse conjunto de seis, pois devido as características citadas anteriormente (mesma interface e não alteração de estado) seria apenas repetição nas outras classes a aplicação da refatoração Limitar Instanciação com Singleton. Logo, segue código da classe PermissaoRequisitada, uma das seis classes:

public class PermissaoRequisitada extends Permissao {
public static final String NOME = "REQUISITADA";

public String nome(){
return NOME;
}

public void reivindicadoPor( SistemaAdmin admin, SistemaPermissao permissao ){
permissao.vaiSerAministradoPor( admin );
permissao.setEstado( permissao );
}
}

 

Agora podemos seguir com a mecânica de refatoração.

Mecânica

Nosso primeiro passo é identificar nossa classe que está sendo instanciada várias vezes e não apresenta mudança de estado (atributo) ou o estado dela é compartilhado. Já sabemos que são seis classes (aqui vamos refatorar somente uma como suficiente para entendimento do método de refatoração).

Mas espere ai, você não falou sobre estado compartilhado?

Estado compartilhado é muitas vezes aplicado via atributo estático, onde todas as instâncias da classe têm exatamente o mesmo valor, para atributos estáticos. A outra forma é por meio de injeção de dependência, a grosso modo, por meio de parâmetros de métodos ou construtores das classes, onde os parâmetros sendo utilizados em uma instância são os mesmos sendo utilizados em outras instâncias.

Resumo: os valores sendo utilizados nos atributos das instâncias, seguindo os contextos indicados anteriormente, esses são valores compartilhados, estados compartilhados.

Note que no método reivindicadoPor() da classe PermissaoRequisitada há objetos sendo injetados na instância, injetados como parâmetros de entrada.

Como esses estão entrando no objeto do tipo PermissaoRequisitada e não sendo instanciados dentro dele e juntando a isso o conhecimento de que as classes irmãs de PermissaoRequisitada e a própria nunca têm estado alterado depois de vinculado os valores de parâmetros de entrada.

Sabendo disso podemos concluir que qualquer instância de PermissaoRequisitada ou de suas classes irmãs compartilham os mesmos valores de atributos, depois desses inicializados.

Ainda no primeiro passo, devemos, na classe identificada, aplicar o método de refatoração Substituir Construtores por Métodos de Criação. Mesmo quando tem somente um único construtor e esse sendo o construtor padrão da linguagem.

Logo depois desse primeiro passo temos o seguinte novo código em PermissaoRequisitada:

public class PermissaoRequisitada {
...
public static Permissao instancia(){
return new PermissaoRequisitada();
}
...
}

 

Ok, mas por que retornar um tipo Permissao no método de criação?

Nesse caso, em especifico, porque são seis classes na mesma hierarquia com a classe Permissao sendo a superclasse, e todas essas classes representam estados de outros objetos, logo, para não ter de trabalhar com variáveis de cada um dos seis tipos nesses objetos clientes, utilizamos apenas o tipo Permissao que nos permite manter as particularidades de cada classe de estado, porém sem a sobrecarga de varáveis especificas do tipo Permissao, somente uma do tipo Permissao é utilizada.

Ainda no primeiro passo temos de substituir as chamadas a instanciação de nossa classe PermissaoRequisitada pela chamada ao método criador, getInstance(). Isso nos códigos clientes. Segue exemplo de um desses algoritmos cliente:

public class SistemaPermissao {
private Permissao estado;
...
public SistemaPermissao( SistemaUsuario requisitor, SistemaPerfil perfil ){

this.requisitor = requisitor;
this.perfil = perfil;
/* state = new PermissaoRequisitada() */
state = PermissaoRequisitada.instancia();
...
}
...
}

 

Nosso segundo passo é criar um atributo singleton, ele será estático, privado, do mesmo tipo da classe e já na declaração dessa variável podemos atribuir uma instância, mas em nosso caso vamos evitar essa última estratégia. Então temos:

public class PermissaoRequisitada {
private static Permissao state;
...
}

 

Nosso terceiro e último passo é fazer com que nosso método de criação, getInstance(), retorne nosso atributo singleton. Logo, temos:

public class PermissaoRequisitada {
...
public static Permissao instancia(){

if( state == null ){
state = new PermissaoRequisitada();
}
return state;
}
...
}

Se em seu caso for necessário algum parâmetro, pode colocá-lo como parâmetro de entrada em seu método de criação, não há problemas quanto aos passos do método de refatoração ao fazer isso em seu código.

Abaixo a classe atualizada:

public class PermissaoRequisitada {
public static final String NOME = "REQUISITADA";

private static Permissao state;

public static Permissao instancia(){

if( state == null ){
state = new PermissaoRequisitada();
}
return state;
}

public String nome(){
return NOME;
}

public void reivindicadoPor( SistemaAdmin admin, SistemaPermissao permissao ){
permissao.vaiSerAministradoPor( admin );
permissao.setEstado( permissao );
}
}

 

O que deveríamos fazer agora, se estivéssemos com o projeto real em mãos, seria atualizar todas as outras classes de estado de permissão para se tornarem classes Singletons.

Um passo extra e que considero muito importante é não permitir instanciações externas da classe recapturada para um Singleton. No caso de PermissaoRequisitada e das classes irmãs dela isso ainda é possível.

Para esse passo extra apenas vamos adicionar um construtor com modificador de acesso private, isso na classe PermissaoRequisitada. Assim, temos:

public class PermissaoRequisitada extends Permissao {


private Permissao(){
super();
}

}

 

Com era o construtor padrão do Java que estava sendo utilizado, sabemos que somente o super() já atende como conteúdo do construtor, agora private. Assim somente a instanciação em getInstance() é que funcionará.

Recomendo que aplique esse passo extra em todas as refatorações Limitar Instanciação com Singleton.

instance e getInstance()

Esse nomes não foram adotados aleatoriamente. Quando trabalhando com padrões de projeto, nomes de classes, métodos e variáveis podem seguir uma convenção onde qualquer outro programador somente de visualizar a convenção sendo aplicada saberá o tipo de padrão sendo utilizado e consequentemente o modelo de código. Isso é a linguagem universal aplicada pelos padrões de projeto sendo apresentada.

A variável de valor único de instancia em um Singleton e o método de retorno dela, quase sempre, recebem respectivamente os nomes: instance e getInstance(). Mesmo em códigos não em inglês.

Dessa forma terminamos com o método de refatoração proposto aqui.

Conclusão

Apesar de muitos críticos ao uso do Singleton, ele tem suas qualidades. Algumas vezes não precisamos de um medidor de desempenho para enxergar problemas no código.

Visto que uma classe está sendo utilizada em vários pontos do projeto, porém sempre com o mesmo estado, Singleton nela! A melhora pode não ser visível, porém sabemos o problema que pode ocorrer caso uma refatoração não seja aplicada ao código. O principal problema pode ser com a memória do sistema.

Outros artigos da série

Segue abaixo, lista de todos os artigos de refatoração de código presentes no Blog:

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

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

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 o conteúdo exclusivo do Blog, além de promoções de livros e cursos de programação.
Email inválido

Relacionado

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
Refatoração Para PadrõesRefatoração Para PadrõesLivros
Facebook Login, Firebase Android - Parte 5Facebook Login, Firebase Android - Parte 5Android
Google SignIn API, Firebase Android - Parte 6Google SignIn API, Firebase Android - Parte 6Android

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