Refatoração de Código: Introduzir Objeto Nulo

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: Introduzir Objeto Nulo

Refatoração de Código: Introduzir Objeto Nulo

Vinícius Thiengo
(1270)
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 artigo continuamos com a série Refatoração de Código, para códigos de alta performance. Dessa vez vamos falar sobre o método de refatoração Introduzir Objeto Nulo.

Ressalto que os métodos de refatoração e padrões de projeto dessa série podem ser utilizados em qualquer software no paradigma orientado a objetos.

Antes de prosseguir com o conteúdo desse artigo é importante que você saiba o que é o padrão de projeto Objeto Nulo. No artigo Padrão de Projeto: Objeto Nulo falo somente sobre esse padrão, que é bem simples de entender e utilizar. 

Tópicos presentes no artigo:

Método de refatoração Extrair Subclasse

Já explicado em outros artigos, mas caso ainda não conheça esse método de refatoração, segue explanação.

Método bem simples e dispensa um artigo somente para ele. Esse é aplicado quando temos classes respondendo a mais de uma tarefa principal e com isso é possível, na maioria dos casos, extrair ao menos duas novas subclasses (somente uma também é válido). Digo subclasses, pois alguns comportamentos (métodos) e estados (atributos) ainda serão compartilhados, logo, para evitar repetição de código, e manter um contexto consistente, se tornam em subclasses, as novas classes.

Motivação

Seu código tem vários trechos de verificação de valor nulo:

if( variavel != null ){
variavel.fazAlgumaCoisa();
}

 

E esses trechos não têm um significado importante na lógica de negócio de seu sistema.

Significado importante?

Sim, quando parte da lógica requer que ou exista um objeto ou seja nulo. Nesse caso não se utiliza o padrão de projeto Objeto Nulo, pois a lógica vai trabalhar também com valores nulos.

Esses scripts de verificação de null deixam seu código inflado e de certa forma atrapalha o crescimento do projeto quando outros programadores não souberem se devem ou não sempre trabalhar com verificação de valores nulos.

Nesse contexto, aplicar o padrão Objeto Nulo pode aliviar em muito o atraso na evolução do sistema, pois o código para quando há ou não o objeto esperado será exatamente o mesmo, sem condicionais de verificação de null.

Código de exemplo

Vamos utilizar como exemplo um código antigo (que utiliza Applets Java). O código é parte de um algoritmo de um Web site que fazia com que o Menu do site funcionasse utilizando Applets Java para capturar os eventos de mouse dos usuários. Mais precisamente: Applet com uma variável do tipo MouseEventHandler para manipular os eventos de mouse.

Segue trecho do código:

public class NavigationApplet extends Applet {
private MouseEventHandler mouseEventHandler;
...

public boolean mouseMove(Event event, int x, int y){

if( mouseEventHandler != null ){ /* VERIFICAÇÃO DE NULL */
return mouseEventHandler.mouseMove( context, event, x, y );
}
return true;
}

public boolean mouseDown(Event event, int x, int y){

if( mouseEventHandler != null ){ /* VERIFICAÇÃO DE NULL */
return mouseEventHandler.mouseDown( context, event, x, y );
}
return true;
}

public boolean mouseUp(Event event, int x, int y){

if( mouseEventHandler != null ){ /* VERIFICAÇÃO DE NULL */
return mouseEventHandler.mouseUp( context, event, x, y );
}
return true;
}

public boolean mouseExit(Event event, int x, int y){

if( mouseEventHandler != null ){ /* VERIFICAÇÃO DE NULL */
return mouseEventHandler.mouseExit( context, event, x, y );
}
return true;
}
...
}

 

Se você pensou algo como: Mas não faço a mínima ideia do que seja um Applet!

Não há problemas, nem mesmo vou explicar ele aqui, pois, além de não atrapalhar no entendimento da refatoração, esse recurso não mais é utilizado. O que tem de entender é como utilizar o padrão de projeto Objeto Nulo em um código já existente, isso aplicando o método proposto nesse artigo.

Como curiosidade, o Applet, para Web sites, era algo muito similar ao que é o Ajax hoje.

Bom, com isso podemos prosseguir com a refatoração em si.

Mecânica

Nosso primeiro passo no código de exemplo é aplicar a refatoração Extrair Subclasse (a explicação desse método de refatoração está logo no início do artigo, ele é bem simples).

Mas extrair subclasse de onde?

De nossa classe fonte. A classe fonte é a classe na qual vamos criar uma classe Objeto Nulo.

Em nosso caso, a classe fonte é a MouseEventHandler. Note que não há necessidade de apresentar o código dessa classe aqui e sim o código de alguma classe cliente, como fizemos com a classe NavigationApplet.

Com isso, ainda no primeiro passo da refatoração, temos a nova classe, a classe que representa o Objeto Nulo, NullMouseEventHandler:

public class NullMouseEventHandler {
public NullMouseEventHandler( Context context ){
super( context );
}
}

 

Veja que já implementamos o construtor e que ele têm um parâmetro de entrada para preencher o construtor de MouseEventHandler.

Esse parâmetro é opcional, não precisamos desse construtor com parâmetro de entrada, apesar de ser uma boa prática deixar todos os métodos e construtores públicos da classe de objeto nulo com a exata mesma interface da classe herdada ou Interface implementada (essa última você não tem escolha, terá de ter a mesma interface).

Logo, o código da classe NullMouseEventHandler abaixo, também seria válido:

public class NullMouseEventHandler {
public NullMouseEventHandler(){
super( null );
}
}

 

Mesmo sabendo que você seguiu a dica do início do artigo e primeiro leu do Objeto Nulo, ressalto que poderíamos ter implementado uma Interface ao invés de herdado uma classe. Nesse caso, a Interface deveria ser a responsável por todos os métodos públicos em MouseEventHandler, caso contrário poderíamos estar criando uma. Aqui vamos seguir somente com a herança de nossa classe fonte.

Seguramente podemos prosseguir para o segundo passo da refatoração. Nesse passo dois, devemos buscar ao menos uma verificação de null para nossa variável de instância mouseEventHandler.

Nada de difícil, logo em mouseMove() temos essa verificação:

...
public boolean mouseMove(Event event, int x, int y){

if( mouseEventHandler != null ){ /* VERIFICAÇÃO DE NULL */
return mouseEventHandler.mouseMove( context, event, x, y );
}
return true;
}
...

 

O que devemos entender nesse passo da mecânica é que o método mouseMove() de mouseEventHandler, deve ser implementado pela classe de objeto nulo, NullMouseEventHandler, e o conteúdo dessa implementação deve ser o código alternativo executado caso mouseEventHandler seja null. Nesse caso é o simples return true. Segue código de NullMouseEventHandler atualizado:

public class NullMouseEventHandler {
...
public boolean mouseMove(MetaGraphicsContext mgc, Event event, int x, int y){
return true;
}
}

 

No terceiro passo temos que aplicar o passo dois em todas as outras verificações de null do projeto para a variável do tipo MouseEventHandler. Logo, a classe NullMouseEventHandler agora fica da seguinte forma:

public class NullMouseEventHandler {
...
public boolean mouseMove(MetaGraphicsContext mgc, Event event, int x, int y){
return true;
}

public boolean mouseDown(MetaGraphicsContext mgc, Event event, int x, int y){
return true;
}

public boolean mouseUp(MetaGraphicsContext mgc, Event event, int x, int y){
return true;
}

public boolean mouseExit(MetaGraphicsContext mgc, Event event, int x, int y){
return true;
}
}

 

Você provavelmente deve estar se perguntando: e se não houvesse códigos caso mouseEventHandler fosse null? O que colocaria como conteúdo desse método em NullMouseEventHandler?

Nesse caso, não colocaria nada. Um exemplo é o método doMouseClick() que também faz parte do código de exemplo (tinha sido omitido até aqui). Segue:

public class NavigationApplet extends Applet {
...
public void doMouseClick(Event event, String imageMapName, String APID){

if( mouseEventHandler != null ){ /* VERIFICAÇÃO DE NULL */
return mouseEventHandler.doMouseClick(imageMapName, APID);
}
}
}

 

Em NullMouseEventHandler ele ficaria:

public class NullMouseEventHandler {
...
public void doMouseClick(Event event, String imageMapName, String APID){}
}

 

Exatamente assim: vazio.

No quarto passo devemos inicializar a variável, local ou de instância, que é utilizada na verificação de valor nulo. A inicialização deve ser com uma instância da classe de Objeto Nulo.

Porém toda essa alteração no código não pode influenciar em nada no código que atribui uma instância da classe fonte (quando o objeto dessa classe está pronto) a variável que inicializará com uma instância de Objeto Nulo.

Em nosso caso a variável é a mouseEventHendler e logo na declaração dela, dentro da classe, podemos inicializa-la com uma infância de objeto nulo:

public class NavigationApplet extends Applet {
private MouseEventHandler mouseEventHandler = new NullMouseEventHandler(null);
...
}

 

Note que a instância do objeto nulo atribuída a variável de instância no código anterior não atrapalha em nada a atribuição de um novo valor, mas não nulo, a essa variável.

Somente assegure-se de remover ou atualizar códigos do tipo:


if( mouseEventHandler == null ){
mouseEventHandler = getInstanciaRealMouseEventHandle();
}

 

No código anterior, dependendo da lógica de negócio de seu projeto, pode remover o condicional e deixar somente o corpo dele. Caso contrário terá que mudar para algo similar a:


if( mouseEventHandler instanceof NullMouseEventHandler ){
mouseEventHandler = getInstanciaRealMouseEventHandle();
}

 

Comentei sobre os códigos anteriores mais para lhe deixar ciente que esse tipo de coisa pode acontecer, nesses casos pode ser que não seja possível remover o condicional. Isso não indica refatoração ruim, pois muito provavelmente a maioria das verificações de nulo de seu algoritmo é para evitar processamento com variável não inicializada, e esses condicionais poderão sim ser removidos.

Agora, no quinto passo, somente apagamos todas as ocorrências de condicionais de verificação de nulo para nossa variável do tipo da classe fonte, mouseEventHandler. Note que devemos apagar o condicional e suas chaves de bloco e não o conteúdo do condicional. Segue nova versão de NavigationApplet:

public class NavigationApplet extends Applet {
private MouseEventHandler mouseEventHandler = new NullMouseEventHandler(null);

public boolean mouseMove(Event event, int x, int y){
return mouseEventHandler.mouseMove( context, event, x, y );
}

public boolean mouseDown(Event event, int x, int y){
return mouseEventHandler.mouseDown( context, event, x, y );
}

public boolean mouseUp(Event event, int x, int y){
return mouseEventHandler.mouseUp( context, event, x, y );
}

public boolean mouseExit(Event event, int x, int y){
return mouseEventHandler.mouseExit( context, event, x, y );
}

public void doMouseClick(Event event, String imageMapName, String APID){
mouseEventHandler.doMouseClick(imageMapName, APID);
}
}

 

O último e sexto passo é nada mais nada menos que a re-aplicação dos passos 4 e 5 em todas as outras classes clientes de objetos do tipo MouseEventHandler e que tenham verificações de null para a variável desse tipo.

Conclusão

Se encontrado, em seu código, trechos de script verificando o valor null para executar ou não uma tarefa, verifique se a utilização de objetos nulos é aceitável e caso sim, aplique esse padrão por meio do método de refatoração proposto aqui. Pois dessa forma seu código, além dos ganhos da aplicação do padrão, vai sempre ser o mesmo, digo, para objetos com valores reais ou com valores vazio ou padrão (vulgo: objeto nulo).

Somente fique atento com os problemas de utilizar superclasse ao invés de Interface.

Outros artigos da série

Abaixo toda a lista de artigos sobre refatoração de código, aqui do Blog:

Internalizar Singleton

Mover Embelezamento Para Decorator

Substituir Condicionais que Alteram Estado por State

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

Substituir Construtores Por Métodos de Criação

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

Padrão de Projeto: Factory MethodPadrão de Projeto: Factory MethodAndroid
Padrão de Projeto: AdapterPadrão de Projeto: AdapterAndroid
Monetização sem Anúncios utilizando a Huq SDKMonetização sem Anúncios utilizando a Huq SDKAndroid
Use a Cabeça! Padrões de ProjetosUse a Cabeça! Padrões de ProjetosLivros

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