Patrones de diseño de comportamiento: Null object

Intención del patrón

Encapsular la ausencia de un objeto mediante la provisión de una alternativa sustituible que ofresca un valor por defecto con comportamiento "No hacer nada". En resumen, un diseño en donde "nada resultará de nada".
Usar el patrón Null object cuando:

  • Un objeto requiera de un colaborador. El patrón Null object no introduce esta colaboración; hace que se use una colaboración que ya existía.
  • Alguna de las intancias del colaborador no haga nada.
  • Se desee abstraer el manejo del "null" fuera del cliente.

Problema de ejemplo

Dado que una referencia a un objeto puede opcionalmente ser nula y que el resultado de la verificación de ser null es no hacer nada o usar algún valor por defecto ¿Cómo puede la ausencia de un objeto (la presencia de una referencia nula) ser tratada de manera transparente?

Discusión

Algunas veces una clase que requiere un colaborador, no necesita que ese colaborador no haga nada. De cualquier manera, las clases desean tratar a los colaboradores no hacen nada de la misma manera que lo hacen con aquellos que realmente proveen un comportamiento.
Consideremos por ejemplo un simple screen saver el cual muestra bolas que se mueven por la pantalla y tienen colores con efectos especiales. Esto se logra fácilmente mediante la creación de una clase Ball para representar las bolas y usando el patrón Strategy para controlar el movimiento de las bolas y otro patrón Strategy para controlar el color.
Podría resultar trivial escribir Strategies para diferentes de movimiento y efectos de color, y así crear bolas con cualquier combinación de ellos. Sin embargo, para comenzar, se debe crear los Strategies lo más simple posibles para asegurarse que todo funciona correctamente.


Ahora, el Strategy más simple, podría no ser un Strategy. Esto es "no hacer nada", no mover, no cambiar de color. Sin embargo, el patrón Strategy que la la bola posea objetos que implementen la interfaz del Strategy. Aquí es donde el patrón Null object se convierte en útil.
Simplemente implementando NullMovementStrategy el cual no mueve la bola NullColorStrategy que no hace cambiar de color la bola. Ambos, probablemente, pueden ser implementados esencialmente sin código. Todos los métodos en estas clases "no hacen nada" y son perfectos ejemplos del patrón de diseño Null object.
La clave del patrón Null object es una clase abstracta que define la interfaz para todos los objetos de este tipo. El patrón es implementado como una clase derivada de dicha clase abstracta. Por este motivo puede ser usada en cualquier lugar que este tipo de objeto sea necesario. En contraposición a usar un valor especial "null" el cual en realidad no implementa la interfaz abstracta y debe ser verificado constantemente por código especialmente enfocado a ello en todos los objetos que usen la interfaz abstracta.
Algunas veces se piensa que este patrón es demasiado simple o "estúpido" pero a rigor de verdad un Null object siempre sabe exactamente qué necesita para ser llevado a cabo sin interactuar con otros objetos. A ciencia cierta, es muy "inteligente".

Estructura



Client
  • Requiere un colaborador.
AbstractObject
  • Declara la interfaz para los colaboradores de Client.
  • Implementa el comportamiento por defecto para la interfaz común a todas las clases.
RealObject
  • Define una clase derivada concreta de AbstractObject cuya instacia proporciona comportamiento útil que Client espera.
NullObject
  • Proporciona una interfaz idéntica a AbstractObject para que un objeto nulo pueda ser sustituido por un objeto real.
  • Implementa la interfaz para "no hacer nada". Qué es lo que exactamente significa "no hacer nada", depende de la clase de comportamiento que Client espera.
  • Cuando existe más de una manera de "no hacer nada", más de una clase NullObject puede ser necesaria.

Reglas prácticas


  • La clase Null object es a menudo implementada como un Singleton. Ya que un objeto nulo usualmente no tiene ningún estado, su estado no puede cambiar, entonces múltiples instancias tienen son idénticas. En lugar de usar múltiples instancias idénticas, el sistema puede usar sólo una única instancia repetidamente.
  • Si algunos clientes esperan que el objeto nulo no haga nada de una manera y otra, múltiples clases Null object serán necesarias. Si el comportamiento de nohacer nada debe ser configurado en tiempo de ejecución, la clase Null object requerirá variables adicionales para que el cliente pueda especificar cómo Null object debería "no hacer nada" (ver el apartado Discusión en el patrón Adapter). Esto puede ser, generalemente, un síntoma de que AbstractObject no tiene una interfaz bien definida.
  • Un Null object no se trasnforma para convertirse en un objeto real. Si el objeto puede decidir dejar de no hacer nada para comenzar tener un comportamiento real, este no es un Null object. Es un objeto real con un modo de no hacer nada, como un Controller el cual puede enceder y apagar el modo Read only. Si éste es un objeto único que debe mutar de un objeto que no hace nada a uno real, pues debe ser implementado con el patrón State o tal vez el patrón Proxy. En este caso, puede ser usado un Null state o el Proxy puede contener un Null object.
  • El uso de un objeto puede ser similar a un Proxy, pero ambos patrones tienen diferentes propósitos. Un Proxy proporciona un nivel de indirección cuando se accede a un sujeto real, y así se controla el acceso al sujeto. Un colaborador nulo no esconde un objeto real y controla el acceso a éste, sino que reemplaza el objeto real. Un Proxy puede eventualmente cambiar y comenzar a actuar como el objeto real. Un Null objet no cambia para comenzar a proporcionar un comportamiento real sino que siempre proveerá un comportamiento de "no hacer nada".
  • Un Null object puede ser un caso especial del patrón Strategy. El patrón Strategy especifica varias clases concretas como diferentes enfoques para cumplir con una tarea. Si uno de estos puntos de vista es consistente con "no hacer nada", dicha clase concreta es un Null object. Por ejemplo, un Controller es un Strategy de vista paa manipular entradas y un NoController sería aquel que ignora todas las entradas.
  • Un Null object puede ser un caso especial del patrón State. Normalmente, cada Sate concreto posee algunos métodos que no hacen nada debido a que ellos no son apropiados para el estado actual. De hecho, un método dado es a menudo implementado para realiar algo útil para la mayoría de los estados, pero para no hacer nada en al menos uno de los estados. Si un State concreto en particular implementa la mayoría de sus métodos para no hacer nada o al menos para retornar valores nulos, se convierte en un State que no hace nada y como tal, es un estado nulo.
  • Un Null object puese ser usado para permitir a un Visitor "visitar" sin problemas una jerarquía y manipular una situación nula.
  • Null object es un colaborador concreto que actúa como un colaborador que el cliente necesita. El comportamiento null no se diseña para ser mezclado dentro de un objeto que necesita algún comportamiento que no haga nada. Está diseñado para una clase que delega a un colaborador todo el comportamiento que puede o no, que no haga nada

Ejemplo de código en Java

class NullOutputStream extends OutputStream {
 public void write(int b) {
  // Do nothing
 }
}

class NullPrintStream extends PrintStream {
 public NullPrintStream() {
  super(new NullOutputStream());
 }
}

class Application {
 private PrintStream debugout;
 public Application(PrintStream debugout) {
  this.debugout = debugout;
 }

 public void go() {
  int sum = 0;
  for (int i = 0; i < 10; i++) {
   sum += i;
   debugout.println("i = " + i);
  }
  System.out.println("sum = " + sum);
 }
}

0 comentarios:

Publicar un comentario

Muchas gracias por leer el post y comentarlo.

 
Copyright 2009 Programación SOLIDa
BloggerTheme by BloggerThemes | Design by 9thsphere