Saltar al contenido principal

Decorator

StructuralGang of FourExtensibilityAlrededor de 2 min

También conocido como

Wrapper

Propósito

Adjunte responsabilidades adicionales a un objeto de forma dinámica. Los decoradores proporcionan una alternativa
flexible a la subclase para ampliar la funcionalidad.

Explicación

Ejemplo real

En las colinas cercanas vive un trol furioso. Normalmente va con las manos desnudas, pero a veces lleva un arma. Para
armar al troll no es necesario crear un nuevo troll sino decorarlo dinámicamente con un arma adecuada.

En pocas palabras

El patrón decorador permite cambiar dinámicamente el comportamiento de un objeto en tiempo de ejecución envolviéndolo
en un objeto de una clase decoradora.

Wikipedia says

En programación orientada a objetos, el patrón decorador es un patrón de diseño que permite añadir comportamiento a un
objeto individual, ya sea de forma estática o dinámica, sin afectar al comportamiento de otros objetos de la misma
clase. El patrón decorador suele ser útil para adherirse al Principio de Responsabilidad Única, ya que permite dividir
la funcionalidad entre clases con áreas de interés únicas, así como al Principio Abierto-Cerrado, al permitir extender
la funcionalidad de una clase sin modificarla.

Ejemplo programático

Tomemos el ejemplo del troll. En primer lugar tenemos un SimpleTroll que implementa la interfaz Troll:

public interface Troll {
  void atacar();
  int getPoderAtaque();
  void huirBatalla();
}

@Slf4j
public class SimpleTroll implements Troll {

  @Override
  public void atacar() {
    LOGGER.info("¡El troll intenta atraparte!");
  }

  @Override
  public int getPoderAtaque() {
    return 10;
  }

  @Override
  public void huirBatalla() {
    LOGGER.info("¡El troll chilla de horror y huye!");
  }
}

A continuación, queremos añadir un palo para el troll. Podemos hacerlo de forma dinámica mediante el uso de un
decorador:

@Slf4j
public class TrollConGarrote implements Troll {

  private final Troll decorado;

  public TrollConGarrote(Troll decorado) {
    this.decorado = decorado;
  }

  @Override
  public void atacar() {
    decorado.atacar();
    LOGGER.info("¡El troll te golpea con un garrote!");
  }

  @Override
  public int getPoderAtaque() {
    return decorado.getPoderAtaque() + 10;
  }

  @Override
  public void huirBatalla() {
    decorado.huirBatalla();
  }
}

Aquí está el troll en acción:

// troll simple
LOGGER.info("Un troll de aspecto simple se acerca.");
var troll = new SimpleTroll();
troll.atacar();
troll.huirBatalla();
LOGGER.info("Poder del troll simple: {}.\n", troll.getPoderAtaque());

// cambia el comportamiento del troll simple agregando un decorador
LOGGER.info("Un troll con un enorme garrote te sorprende.");
var trollConGarrote = new TrollConGarrote(troll);
trollConGarrote.atacar();
trollConGarrote.huirBatalla();
LOGGER.info("Poder del troll con garrote: {}.\n", trollConGarrote.getPoderAtaque());

Salida del programa:

Un troll de aspecto simple se acerca. 
¡El troll intenta atraparte!
¡El troll chilla de horror y huye!
Poder del troll simple: 10.

Un troll con un enorme garrote te sorprende.
¡El troll intenta atraparte!
¡El troll te golpea con un garrote!
¡El troll chilla de horror y huye!
Poder del troll con garrote: 20.

Diagrama de clases

alt text
Decorator pattern class diagram

Aplicabilidad

Decorator se utiliza para:

  • Añadir responsabilidades a objetos individuales de forma dinámica y transparente, es decir, sin
    afectar a otros objetos.
  • Para responsabilidades que pueden ser retiradas.
  • Cuando la extensión por subclase es poco práctica. A veces es posible un gran número de extensiones independientes
    son posibles y producirían una explosión de subclases para soportar cada combinación. O la definición de una clase
    puede estar oculta o no estar disponible para subclases.

Tutoriales

Usos conocidos

Créditos