Introducción al patrón de objeto nulo

1. Información general

En este tutorial rápido, veremos el patrón de objeto nulo, un caso especial del patrón de estrategia. Describiremos su propósito y cuándo deberíamos considerar su uso.

Como de costumbre, también proporcionaremos un ejemplo de código simple.

2. Patrón de objeto nulo

En la mayoría de los lenguajes de programación orientados a objetos, no podemos utilizar una referencia nula . Es por eso que a menudo nos vemos obligados a escribir comprobaciones nulas :

Command cmd = getCommand(); if (cmd != null) { cmd.execute(); }

A veces, si el número de tales declaraciones if aumenta, el código puede volverse feo, difícil de leer y propenso a errores. Aquí es cuando el Patrón de objeto nulo puede resultar útil.

La intención del Patrón de objeto nulo es minimizar ese tipo de verificación nula . En cambio, podemos identificar el comportamiento nulo y encapsularlo en el tipo esperado por el código del cliente. La mayoría de las veces, esa lógica neutral es muy simple: no hacer nada. De esta forma ya no es necesario que nos ocupemos de un manejo especial de referencias nulas .

Simplemente podemos tratar los objetos nulos de la misma manera que tratamos cualquier otra instancia de un tipo dado que en realidad contenga una lógica empresarial más sofisticada. En consecuencia, el código del cliente se mantiene más limpio.

Como los objetos nulos no deberían tener ningún estado, no es necesario crear instancias idénticas varias veces. Por lo tanto, a menudo implementaremos objetos nulos como singleton .

3. Diagrama UML del patrón de objeto nulo

Veamos el patrón visualmente:

Como vemos, podemos identificar a los siguientes participantes:

  • El cliente requiere una instancia de AbstractObject
  • AbstractObject define el contrato que espera el cliente ; también puede contener lógica compartida para las clases de implementación
  • RealObject implementa AbstractObject y proporciona un comportamiento real
  • NullObject implementa AbstractObject y proporciona un comportamiento neutral

4. Implementación

Ahora que tenemos una idea clara de la teoría, veamos un ejemplo.

Imagina que tenemos una aplicación de enrutador de mensajes. Cada mensaje debe tener asignada una prioridad válida. Se supone que nuestro sistema enruta los mensajes de alta prioridad a una puerta de enlace SMS, mientras que los mensajes con prioridad media deben enrutarse a una cola JMS.

Sin embargo, de vez en cuando, pueden llegar a nuestra aplicación mensajes con prioridad "indefinida" o vacía . Estos mensajes deben descartarse del procesamiento posterior.

Primero, crearemos la interfaz del enrutador :

public interface Router { void route(Message msg); }

A continuación, creemos dos implementaciones de la interfaz anterior: la responsable del enrutamiento a una puerta de enlace SMS y la que enrutará los mensajes a la cola 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, implementemos nuestro objeto nulo:

public class NullRouter implements Router { @Override public void route(Message msg) { // do nothing } }

Ahora estamos listos para juntar todas las piezas. Veamos cómo puede verse el código de cliente de ejemplo:

public class RoutingHandler { public void handle(Iterable messages) { for (Message msg : messages) { Router router = RouterFactory.getRouterForMessage(msg); router.route(msg); } } }

Como podemos ver, tratamos todos los objetos Router de la misma manera, sin importar qué implementación sea devuelta por RouterFactory. Esto nos permite mantener nuestro código limpio y legible.

5. Cuándo usar el patrón de objeto nulo

Deberíamos usar el Patrón de objeto nulo cuando un Cliente, de lo contrario, buscaría un valor nulo solo para omitir la ejecución o realizar una acción predeterminada. En tales casos, podemos encapsular la lógica neutral dentro de un objeto nulo y devolverlo al cliente en lugar del valor nulo . De esta forma, el código del cliente ya no necesita saber si una instancia determinada es nula o no.

Este enfoque sigue principios generales orientados a objetos, como Decir-No-Preguntar.

Para comprender mejor cuándo debemos usar el patrón de objeto nulo, imaginemos que tenemos que implementar la interfaz CustomerDao definida de la siguiente manera:

public interface CustomerDao { Collection findByNameAndLastname(String name, String lastname); Customer getById(Long id); }

La mayoría de los desarrolladores devolverían Collections.emptyList () de findByNameAndLastname () en caso de que ninguno de los clientes coincida con los criterios de búsqueda proporcionados. Este es un muy buen ejemplo de seguir el patrón de objeto nulo.

Por el contrario, get ById () debería devolver al cliente con la identificación dada. Alguien que llama a este método espera obtener la entidad de cliente específica. En caso de que no exista tal cliente, debemos devolver explícitamente null para indicar que hay algún problema con la identificación proporcionada.

Al igual que con todos los demás patrones, debemos considerar nuestro caso de uso específico antes de implementar ciegamente el Patrón de objeto nulo . De lo contrario, podemos introducir involuntariamente algunos errores en nuestro código que serán difíciles de encontrar.

6. Conclusión

En este artículo, aprendimos qué es el patrón de objeto nulo y cuándo podemos usarlo. También implementamos un ejemplo simple del patrón de diseño.

Como de costumbre, todos los ejemplos de código están disponibles en GitHub.