Qué es el patron de diseño Decorator

Qué es el patron de diseño Decorator

Siguiendo nuestro curso de programación hoy veremos el patrón Decorator. El patrón estructural Decorator permite añadir responsabilidades a un objeto dinámicamente. Los decoradores proporcionan una alternativa flexible a la herencia para extender la funcionalidad.

decorator-

Aplicaciones

Se debe usar el patrón decorador cuando:

  • Se quiera añadir responsabilidades a otros objetos dinámicamente y de forma transparente.
  • No se pueda heredar o no resulte práctico.

Aspectos clave

  • Los decoradores tienen el mismo tipo que los objetos que decoran. Por tanto podemos pasar un decorador en vez del objeto original. El decorador sigue cumpliendo la interfaz del objeto original, así que su presencia es transparente para los clientes del componente.
  • Se puede usar uno o más decoradores para envolver un objeto.
  • El decorador añade su propio comportamiento antes y después de delegar al objeto decorado el resto del trabajo.
  • Los objetos pueden ser decorados en cualquier momento. Podemos decorar objetos dinámicamente en tiempo de ejecución con tantos decoradores como queramos y en cualquier orden.

Estructura

estructura_decorator

  • Component: define la interfaz de los objetos a los que se les puede añadir responsabilidades dinámicamente.
  • Decorator: mantiene una referencia a un objeto Component (como por ejemplo con una instancia como veremos en el ejemplo) y tiene su misma interfaz.
  • ConcreteDecorator: añade responsabilidades al componente.

Ventajas

  • Más flexibilidad que la herencia estática.
  • Evita que las clases de arriba de la jerarquía estén repletas de funcionalidades. En vez de definir una clase compleja que trata de dar cabida a todas ellas, la funcionalidad se logra añadiendo decoradores a una clase simple.

Desventajas

  • Un decorador y sus componentes no son idénticos, desde el punto de vista de la identidad de objetos, desde el punto de vista del programador o del cliente si que se podrían considerar iguales.
  • Muchos objetos pequeños. El sistema puede ser más difícil de aprender y de depurar.

Ejemplo

Supongamos un Documento que queremos que pueda ser codificado de varias formas posibles. Vamos a implementarlo mediante una clase principal Documento que asumirá el rol de Component, el Decorador que será el DocumentoAbstracto y las distintas posibles formas de codificarlo que serán los ConcreteDecorator.

uml_decorator

class Documento {    public void operacion() {        System.out.println("Creando documento");    }    public void close() {        System.out.println("Cerrando documento");    }}abstract class DocumentoCodificado extends Documento {    private Documento documento;    public DocumentoCodificado(Documento componente) {        this.documento = componente;    }    public void operacion() {        documento.operacion();    }}class DecoradorConcretoA extends DocumentoCodificado {    public DecoradorConcretoA(Documento componente) {        super(componente);    }    public void operacion() {        super.operacion();        System.out.println("Separador al documento");    }}class DecoradorConcretoB extends DocumentoCodificado {    public DecoradorConcretoB(Documento componente) {        super(componente);    }    public void operacion() {        super.operacion();        System.out.println("Contando lineas del documento");    }}

La forma de instanciar estas clases sería:

public class Prueba {    public static void main(String[] args) {        Documento c = new Documento();        DecoradorConcretoA d1 = new DecoradorConcretoA(c);        DecoradorConcretoB d2;        d2 = new DecoradorConcretoB(d1);        d2.operacion();        d2.close();    }}

Tenemos que tener claro existen diversas formas de usar un decorator, en este caso mediante una instancia del objeto. ¿Se os ocurre alguna otra forma de utilizarlo? ¿Algún otro ejemplo dentro de la API de Java donde se empleen?

Como siempre os dejo el código empleado en el ejemplo.

Para ti
Queremos saber tu opinión. ¡Comenta!