Conforme comentado no outro post, vou descrever aqui outra forma de evitar a verificação de um objeto nulo. Este tipo de verificação que é tão usada causa um bad smell tremendo.
Imagine a seguinte funcionalidade de um sistema de vendas e-commerce:
Quando nós enviarmos um pedido para um cliente que comprou em nossa loja, nós queremos que este cliente seja avisado que seu produto já foi deixado nos correios. Gostariamos de enviar um SMS para os clientes que consideramos ótimo e um e-mail para aqueles que achamos que são bons clientes. Qualquer outro cliente não deve receber o aviso, se ele tiver interesse ele passa no site e verifica o status.
Então, modelando esta user story, podemos ter uma interface chamada de Notificador e algumas implementações como NotificadorViaSms e NotificadorViaEmail. Isso nos permite que em um dado momento possamos incluir algum outro tipo de notificador, por exemplo, NotificadorViaTwitter.
Pois bem, tendo múltiplas implementações eu vou precisar de um factory que vai ser responsável por criar um notificador baseado no tipo do cliente. Aqui vai um exemplo do código:
public class NotificadorFactory {
public static Notificador cria(Pedido pedido) {
if (pedido.getCliente().getTipo() == TipoCliente.Otimo)
return new NotificadorPorSms(pedido);
else if (pedido.getCliente().getTipo() == TipoCliente.Bom)
return new NotificadorPorEmail(pedido);
else
return null;
}
}
Seu uso ficaria assim:
Cliente maria = new Cliente("Maria", TipoCliente.Otimo);
Pedido pedido2 = new Pedido(maria);
pedido2.setTotal(520.10f);
Notificador notificador = NotificadorFactory.cria(pedido2);
notificador.enviar();
Então, o que acharam? Ficou legal, não? Eu gostei :) (só não gostei do nome da factory e do seu método, mas não é esse o objetivo).
Só que este código tem um problema. E se a cliente “maria” fosse do tipo Ruim ao invés de Otimo? Segundo a minha user story, eu não deveria avisar ela. Minha factory está correta, se o cliente for do tipo Ruim ela não vai retornar nenhum Notificador, vai retornar null. Quando meu código cliente for chamar o método enviar() vai surgir um NullPointerException.
Solução: coloca um “if notificador != null” antes de chamar o método. Yeah! Resolvemos o problema, mas criamos outro… Em todo lugar que você for usar o NotificadorFactory você vai ter que se lembrar de que ele pode retornar um valor nulo, e por isso será necessário verificar se o retorno é nulo na maioria das chamadas.
Outra solução seria aplicar o Null Object Pattern. Segundo a definição encontrada no Wikipedia:
Original:
In object-oriented computer programming, a Null Object is an object with defined neutral (“null”) behavior. The Null Object design pattern describes the uses of such objects and their behavior (or lack thereof).
Traduzido:
Em programação orientada a objetos, um Objeto Nulo é um objeto que possui comportamento neutro (nulo). O padrão de projeto Null Object descreve o uso de tais objetos e seu comportamento (ou a falta dela).
De forma simplificada, o Objeto Nulo segundo o padrão de projeto é um objeto que não faz nada, que possui um comportamento neutro. Em nosso caso poderiamos ter um notificador chamado NotificadorVazio que seria criado pelo NotificadorFactory ao invés de retornar null. Sendo assim, o código cliente ficaria livre para utilizar o retorno da factory sem se preocupar com NullPointerException pois o retorno nunca seria nulo. Curioso para saber como fica a implementação do NotificadorVazio? Olha que simples:
A Interface:
public interface Notificador {
void enviar();
}
A implementação nula:
public class NotificadorVazio implements Notificador {
@Override
public void enviar() {
//Não fazer nada
}
}
A factory:
public class NotificadorFactory {
public static Notificador cria(Pedido pedido) {
if (pedido.getCliente().getTipo() == TipoCliente.Otimo)
return new NotificadorPorSms(pedido);
else if (pedido.getCliente().getTipo() == TipoCliente.Bom)
return new NotificadorPorEmail(pedido);
else
return new NotificadorVazio();
}
}
Pronto, resolvemos o problema de forma organizada, orientada a objetos e utilizando padrões bem definidos.
Até mais :D
