El patrón de decorador en Java

1. Información general

Se puede utilizar un patrón Decorator para asignar responsabilidades adicionales a un objeto de forma estática o dinámica. Un decorador proporciona una interfaz mejorada para el objeto original.

En la implementación de este patrón, preferimos la composición sobre una herencia, de modo que podamos reducir la sobrecarga de subclases una y otra vez para cada elemento de decoración. La recursividad involucrada con este diseño se puede utilizar para decorar nuestro objeto tantas veces como lo necesitemos.

2. Ejemplo de patrón de decorador

Supongamos que tenemos un objeto de árbol de Navidad y queremos decorarlo. La decoración no cambia el objeto en sí; es solo que además del árbol de Navidad, estamos agregando algunos elementos de decoración como guirnaldas, guirnaldas, adornos para árboles, luces de burbujas, etc.

Para este escenario, seguiremos las convenciones originales de diseño y nomenclatura de Gang of Four. Primero, crearemos una interfaz ChristmasTree y su implementación:

public interface ChristmasTree { String decorate(); }

La implementación de esta interfaz se verá así:

public class ChristmasTreeImpl implements ChristmasTree { @Override public String decorate() { return "Christmas tree"; } }

Ahora crearemos una clase TreeDecorator abstracta para este árbol. Este decorador implementará la interfaz ChristmasTree y mantendrá el mismo objeto. El método implementado desde la misma interfaz simplemente llamará al método decorate () desde nuestra interfaz:

public abstract class TreeDecorator implements ChristmasTree { private ChristmasTree tree; // standard constructors @Override public String decorate() { return tree.decorate(); } }

Ahora crearemos algún elemento de decoración. Estos decoradores extenderán nuestra clase abstracta TreeDecorator y modificarán su método decorate () de acuerdo con nuestro requisito:

public class BubbleLights extends TreeDecorator { public BubbleLights(ChristmasTree tree) { super(tree); } public String decorate() { return super.decorate() + decorateWithBubbleLights(); } private String decorateWithBubbleLights() { return " with Bubble Lights"; } }

Para este caso, se cumple lo siguiente:

@Test public void whenDecoratorsInjectedAtRuntime_thenConfigSuccess() { ChristmasTree tree1 = new Garland(new ChristmasTreeImpl()); assertEquals(tree1.decorate(), "Christmas tree with Garland"); ChristmasTree tree2 = new BubbleLights( new Garland(new Garland(new ChristmasTreeImpl()))); assertEquals(tree2.decorate(), "Christmas tree with Garland with Garland with Bubble Lights"); }

Tenga en cuenta que en el primer objeto tree1 , solo lo decoramos con un solo Garland , mientras que el otro objeto tree2 lo decoramos con un BubbleLights y dos Garlands . Este patrón nos da esta flexibilidad para agregar tantos decoradores como queramos en tiempo de ejecución.

4. Conclusión

En este artículo, echamos un vistazo al patrón de diseño del decorador. Esta es una buena opción en los siguientes casos:

  • Cuando deseamos agregar, mejorar o incluso eliminar el comportamiento o el estado de los objetos
  • Cuando solo queremos modificar la funcionalidad de un solo objeto de la clase y dejar otros sin cambios

El código fuente completo de este ejemplo está disponible en GitHub.