Introdução ao padrão de objeto nulo
1. Visão geral
Neste tutorial rápido, vamos dar uma olhada no padrão de objeto nulo, um caso especial deStrategy Pattern. Descreveremos seu propósito e quando devemos realmente considerar usá-lo.
Como de costume, também forneceremos um exemplo de código simples.
2. Padrão de Objeto Nulo
Na maioria das linguagens de programação orientadas a objetos, não temos permissão para usar uma referêncianull. É por isso que somos frequentemente forçados a escrevernull cheques:
Command cmd = getCommand();
if (cmd != null) {
cmd.execute();
}
Às vezes, se o número dessas instruçõesif ficar alto, o código pode se tornar feio, difícil de ler e sujeito a erros. É quando o Padrão de Objeto Nulo pode ser útil.
The intent of the Null Object Pattern is to minimize that kind of null check. Em vez disso, podemos identificar o comportamento nulo e encapsulá-lo no tipo esperado pelo código do cliente. Mais frequentemente do que não, essa lógica neutra é muito simples - não faça nada. Dessa forma, não precisamos mais lidar com o tratamento especial das referênciasnull.
Simplesmente podemos tratar objetos nulos da mesma forma que tratamos qualquer outra instância de um determinado tipo que realmente contenha alguma lógica de negócios mais sofisticada. Conseqüentemente, o código do cliente permanece mais limpo.
Como objetos nulos não devem ter nenhum estado, não há necessidade de criar instâncias idênticas várias vezes. Assim, vamos frequentementeimplementnull objects assingletons.
3. Diagrama UML do padrão de objeto nulo
Vejamos o padrão visualmente:
Como podemos ver, podemos identificar os seguintes participantes:
-
Client requer uma instância deAbstractObject
-
AbstractObject define o contrato queClient espera - também pode conter lógica compartilhada para as classes de implementação
-
RealObject implementaAbstractObject e fornece comportamento real
-
NullObject implementaAbstractObjecte fornece um comportamento neutro
4. Implementação
Agora que temos uma ideia clara da teoria, vejamos um exemplo.
Imagine que temos um aplicativo roteador de mensagens. Cada mensagem deve ter uma prioridade válida atribuída. Nosso sistema deve rotear mensagens de alta prioridade para um gateway SMS, enquanto as mensagens com prioridade média devem ser roteadas para uma fila JMS.
De vez em quando,however, messages with “undefined” or empty priority might come para nosso aplicativo. Essas mensagens devem ser descartadas de processamento adicional.
Primeiro, vamos criar a interfaceRouter:
public interface Router {
void route(Message msg);
}
A seguir, vamos criar duas implementações da interface acima - a responsável pelo roteamento para um gateway de SMS e a que roteará as mensagens para a fila JMS:
public class SmsRouter implements Router {
@Override
public void route(Message msg) {
// implementation details
}
}
public class JmsRouter implements Router {
@Override
public void route(Message msg) {
// implementation details
}
}
Finalmente,let’s implement our null object:
public class NullRouter implements Router {
@Override
public void route(Message msg) {
// do nothing
}
}
Agora estamos prontos para colocar todas as peças juntas. Vamos ver como o código do cliente de exemplo pode se parecer:
public class RoutingHandler {
public void handle(Iterable messages) {
for (Message msg : messages) {
Router router = RouterFactory.getRouterForMessage(msg);
router.route(msg);
}
}
}
Como podemos ver,we treat all Router objects the same way, não importa qual implementação é retornada porRouterFactory.. Isso nos permite manter nosso código limpo e legível.
5. Quando usar o padrão de objeto nulo
We should use the Null Object Pattern when a Client would otherwise check for null just to skip execution or perform a default action. Nesses casos, podemos encapsular a lógica neutra dentro de um objeto nulo e retorná-lo ao cliente em vez do valornull. Desta forma, o código do cliente não precisa mais saber se uma determinada instância énull ou não.
Tal abordagem segue princípios gerais orientados a objetos, comoTell-Don’t-Ask.
Para entender melhor quando devemos usar o Padrão de Objeto Nulo, vamos imaginar que temos que implementar a interfaceCustomerDao definida da seguinte forma:
public interface CustomerDao {
Collection findByNameAndLastname(String name, String lastname);
Customer getById(Long id);
}
A maioria dos desenvolvedoresreturn Collections.emptyList() from findByNameAndLastname() in case none of the customers matches os critérios de pesquisa fornecidos. Este é um exemplo muito bom de seguir o Padrão de Objeto Nulo.
Em contraste, ogetById() deve retornar o cliente com o id fornecido. Alguém chamando esse método espera obter a entidade específica do cliente. In case no such customer exists we should explicitly return null to signal there is something wrong with the provided id.
Como com todos os outros padrões,we need to consider our specific use case before blindly implementing the Null Object Pattern. Caso contrário, podemos introduzir inadvertidamente alguns erros em nosso código que serão difíceis de encontrar.
6. Conclusão
Neste artigo, aprendemos o que é o Padrão de Objeto Nulo e quando podemos usá-lo. Também implementamos um exemplo simples do padrão de design.
Como de costume, todos os exemplos de código estão disponíveis emGitHub.