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

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

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

Vinícius Thiengo
(2030)
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 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.

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.

Abaixo os tópicos que estaremos abordando em 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 instâ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.

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 toda a lista de artigos sobre refatoração de código, aqui do Blog:

Fonte

Refatoração para Padrões

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

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