¿Existen reglas estrictas y rápidas sobre el uso de este patrón o está destinado únicamente como una forma de lograr una funcionalidad adicional dentro de las llamadas a métodos sin usar la herencia?

He modificado el siguiente ejemplo que tomé de una publicación de SO para demostrar lo que estoy considerando.

 public interface Coffee {
     public double getCost();
     public String getIngredients();
 }
public class SimpleCoffee implements Coffee {
   @Override
   public double getCost() {
    return 1;
   }
   @Override
   public String getIngredients() {
    return "Coffee";
   }
}

 public class CoffeeDecorator implements Coffee {
   protected final Coffee decoratedCoffee;

   public CoffeeDecorator(Coffee c) {
    this.decoratedCoffee = c;
   }
   @Override
   public double getCost() { 
    //you can add  extra functionality here.
    return decoratedCoffee.getCost();
  }
   @Override
   public String getIngredients() {
    //you can add  extra functionality here.
    return decoratedCoffee.getIngredients();
   }

   public boolean methodNotDefinedInInterface() {
       //do something else
       return true;
   }
}

Entonces, con el ejemplo anterior en mente, ¿es viable:

A) usa el café simple siempre que lo creas conveniente sin decorarlo

B) Agregue funcionalidad adicional que no está definida en la interfaz Coffee a los objetos decoradores como methodNotDefinedInInterface()

¿Alguien podría explicar también dónde entra la composición en este patrón, ya que SimpleCoffee es algo que puede existir por derecho propio, pero parece ser el decorador el que realmente 'posee' cualquier objeto?

Aunque sin la clase SimpleCoffee (o alguna implementación concreta de Coffee) el decorador no tiene ningún propósito, por lo que la agregación no parece ser lo que está ocurriendo aquí.

0
berimbolo 20 feb. 2018 a las 12:01

2 respuestas

La mejor respuesta

La descripción del patrón incluye la intención, lo que deja bastante claro para qué es el patrón:

El patrón de decorador se puede utilizar para ampliar (decorar) la funcionalidad de un determinado objeto de forma estática, o en algunos casos en tiempo de ejecución, independientemente de otras instancias de la misma clase, siempre que se realicen algunos trabajos preliminares en tiempo de diseño.

En cuanto a las "reglas estrictas y rápidas", generalmente no creo que haya "reglas estrictas y rápidas" en los patrones. Por ejemplo, si no lo implementa exactamente como lo describió GoF, no habrá una "policía de patrón" que lo castigue. El único punto es que si sigue las pautas clásicas, otros desarrolladores tendrán menos problemas para reconocer patrones en su código.

Tu ejemplo está bastante bien desde mi punto de vista.

SimpleCoffee no es un decorador, por lo que no hay composición allí. CoffeeDecorator tiene decoratedCoffee como componente (aquí tienes tu composición)

1
lexicore 20 feb. 2018 a las 09:24

a) usa el café simple siempre que lo creas conveniente sin decorarlo

Sí, por supuesto.

b) Agregue funcionalidad adicional que no está definida en la interfaz Coffee a los objetos decoradores como el métodoNotDefinedInInterface ()

Puede agregar más métodos como agregar métodos nuevos a la clase SimpleCoffee, pero tenga en cuenta que necesitaría usar esos métodos adicionales en algún lugar de la clase decoradora.


Personalmente, encuentro útil este patrón cuando alguien te da una instancia de Coffee (es decir, no lo instalaste). Si necesita cambiar su comportamiento en tiempo de ejecución, la única forma es envolverlo dentro de otro objeto de tipo Coffee. Aquí es cuando puedes lanzarlo a la clase de decorador. El decorador puede exponer algunos de los comportamientos originales mientras proporciona algunos comportamientos nuevos.

1
Jai 20 feb. 2018 a las 09:23