# Propósito
El patrón Decorator permite agregar responsabilidades adicionales a un objeto de manera dinámica, en tiempo de ejecución, sin alterar la estructura de clases existentes.
Es especialmente útil para extender funcionalidades de manera flexible y reutilizable.
# Problema
La necesidad de extender la funcionalidad de una clase de manera dinámica, sin recurrir a la herencia, que genera una jerarquía rígida y menos flexible.
# Solución
La solución que propone el Decorator es envolver el objeto original en un objeto “decoradorâ€, que agregue la funcionalidad deseada.
-
Decoración de Objetos: Extiende la funcionalidad envolviéndolos con clases decoradoras.
-
Combinación Flexible: Permite combinar funcionalidades adicionales de forma flexible al usar múltiples decoradores.
# Estructura
# Participantes
- Component: Interfaz para objetos a los que se les quiere agregar responsabilidades.
- ConcreteComponent: Objeto que se va a decorar.
- Decorator: Clase abstracta que envuelve un objeto Component.
- ConcreteDecorator: Añade funcionalidades al Component.
# Cuándo Usarlo
-
Agregar responsabilidades dinámicamente y de manera transparente.
-
Cuando la extensión mediante herencia no es viable o práctica.
# Ventajas
-
verified
Mayor Flexibilidad: Responsabilidades dinámicas.
-
verified
Evita clases sobrecargadas: Separa funcionalidades opcionales.
-
verified
Combinación y Reutilización: Fácil mezcla de comportamientos.
# Desventajas
-
warning
Complejidad: Múltiples capas pequeñas.
-
warning
Configuración: Difícil de configurar con muchas capas.
-
warning
Identificación: Difícil ver el objeto base.
# Ejemplo: Cafetería personalizable
Estamos desarrollando un sistema que necesita ofrecer gran variedad de cafés personalizables (leche, azúcar, crema, etc.).
Problema
Crear una clase para cada combinación generaría explosión de clases difícil de mantener.
Solución planteada
Usamos Decorator para agregar dinámicamente ingredientes al café base. Cada decorador añade su propio costo y descripción.
# Código Java
interface Coffee {
double getCost();
String getDescription();
}
class SimpleCoffee implements Coffee {
public double getCost() { return 2.0; }
public String getDescription() { return "Café simple"; }
}
abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) { this.decoratedCoffee = coffee; }
public double getCost() { return decoratedCoffee.getCost(); }
public String getDescription() { return decoratedCoffee.getDescription(); }
}
class WithMilk extends CoffeeDecorator {
public WithMilk(Coffee coffee) { super(coffee); }
public double getCost() { return super.getCost() + 0.5; }
public String getDescription() { return super.getDescription() + ", con leche"; }
}
public class Client {
public static void main(String[] args) {
Coffee myCoffee = new SimpleCoffee();
myCoffee = new WithMilk(myCoffee);
myCoffee = new WithSugar(myCoffee);
System.out.println("Costo: " + myCoffee.getCost());
}
}
# Mapeo Participantes
- Coffee (Component): Interfaz común.
- SimpleCoffee (ConcreteComponent): Componente base.
- CoffeeDecorator (Decorator): Clase base para decoradores.
- WithMilk, WithSugar (ConcreteDecorators): Agregadores de funcionalidad.
- Client: Ensambla cafés con ingredientes.
# Conclusiones
El patrón Decorator da una solución flexible y escalable, facilitando la adición de nuevos ingredientes en el futuro sin modificar el código existente.
# Patrones relacionados
Strategy
Decorator cambia el exterior; Strategy el interior de un objeto.
Composite
Decorator puede verse como un composite con un solo componente.
Adapter
Decorator cambia responsabilidades; Adapter cambia la interfaz.