2009-10-11 47 views
47

Estoy repasando mis patrones de diseño, y un patrón que aún no he usado seriamente en mi codificación es el Patrón Decorador.Cuándo utilizar el patrón decorador?

Entiendo el patrón, pero lo que me gustaría saber son algunos buenos ejemplos concretos de tiempos en el mundo real en los que el patrón del decorador es la solución mejor/óptima/elegante. Situaciones específicas donde la necesidad del patrón de decorador es realmente útil.

Gracias.

Respuesta

31

El patrón decorador se utiliza una gran cantidad de arroyos: se puede envolver una corriente con una corriente para obtener mayor funcionalidad. Lo he visto con el framework .Net, hasta donde sé, esto ocurre en otros lugares. Mi favorito es usar GZipStream en un FileStream, para una mayor compresión.

+16

De manera similar, las transmisiones de Java son solo una colección gigante de decoradores que decoran decoradores que han decorado algo que decora. Mi cara simplemente se derritió. –

+6

Pensé que los streams serían un mejor ejemplo que el ejemplo de café en mi libro Head First: Design Patterns en mi escritorio. –

+1

¿Alguien puede explicar por qué el patrón de decorador es la solución óptima para esta situación? –

3

Zend Framework utiliza el decorador de elementos de formulario

algo más de información: http://framework.zend.com/manual/en/zend.form.decorators.html

+0

Sí, puedo ver cómo tiene sentido usarlo para elementos de formulario. Estoy leyendo un ejemplo similar en la página de wikipedia: http://en.wikipedia.org/wiki/Decorator_Pattern –

+0

¡Feliz de ver un ejemplo de patrón de diseño de decorador que no es un ejemplo de clase de contenedor de flujo todo el tiempo! – kta

20

que he usado recientemente el patrón decorador en un servicio web que utiliza la interfaz siguiente CommandProcessor:

public Command receive(Request request); 
public Response execute(Command command); 
public void respond(Response response); 

Básicamente, el CommandProcessor recibe una solicitud y crea el comando adecuado, ejecuta el comando y crea la adecuada Respuesta, y envía la respuesta. Cuando quise agregar el tiempo y registrarlo, creé un TimerDecorator que usaba un CommandProcessor existente como su componente. El TimerDecorator implementa la interfaz CommandProcessor, pero solo agrega el tiempo y luego llama a su objetivo, que es el verdadero CommandProcessor. Algo como esto:

public class TimerDecorator implements CommandProcessor { 
    private CommandProcessor target; 
    private Timer timer; 

    public TimerDecorator(CommandProcessor processor) { 
     this.target = processor; 
     this.timer = new Timer(); 
    } 

    public Command receive(Request request) { 
     this.timer.start(); 
     return this.target.receive(request); 
    } 

    public Response execute(Command command) { 
     return this.target.execute(command); 
    } 

    public void respond(Response response) { 
     this.target.response(response); 
     this.timer.stop(); 
     // log timer 
    } 

} 

Así que la verdadera CommandProcessor se envuelve dentro de TimerDecorator, y puedo tratar TimerDecorator al igual que el CommandProcessor objetivo, pero ahora se ha añadido la lógica de sincronización.

60

El patrón de decorador se utiliza para agregar funcionalidad adicional a un objeto en particular en lugar de una clase de objetos. Es fácil agregar funcionalidad a una clase completa de objetos mediante la subclasificación de un objeto, pero es imposible extender un solo objeto de esta manera. Con el Patrón Decorador, puede agregar funcionalidad a un solo objeto y dejar otros sin modificar.

En Java, un ejemplo clásico del patrón de decorador es la implementación de Java I/O Streams.

FileReader  frdr = new FileReader(filename); 
LineNumberReader lrdr = new LineNumberReader(frdr); 

El código anterior crea un lector - lrdr - que lee de un archivo y pistas números de línea. La línea 1 crea un lector de archivos (frdr) y la línea 2 agrega el seguimiento del número de línea.

En realidad, le recomiendo encarecidamente que consulte el código fuente de Java para las clases de E/S de Java.

7

El decorador es simple pero extremadamente potente. Es clave para lograr la separación de preocupaciones y es una herramienta esencial para el Principio Abierto Cerrado.Tomemos un ejemplo común de realizar un pedido de un producto:

IOrderGateway 
{ 
    void PlaceOrder(Order order); 
{ 

aplicación principal: AmazonAffiliateOrderGateway

decoradores posibles podrían ser:

  • IncrementPerformanceCounterOrderGateway
  • QueueOrderForLaterOnTimeoutOrderGateway
  • EmailOnExceptionOrderGateway
  • InterceptTestOrderAndLogOrderGateway

Echa un vistazo a here para ver un ejemplo más detallado.

2
  1. para agregar responsabilidades a los objetos individuales de forma dinámica y transparente.
  2. por responsabilidades que pueden ser retiradas.
  3. cuando la extensión por subclases no es práctica. En ocasiones, es posible un gran número de extensiones independientes y produciría una explosión de subclases para admitir todas las combinaciones.
7

Decorator patrón cambia dinámicamente la funcionalidad de un objeto en tiempo de ejecución sin afectar la funcionalidad existente de los objetos.

casos de uso clave:

  1. Añadir funcionalidades/responsabilidades adicionales dinámicamente
  2. Eliminar funcionalidades/responsabilidades dinámicamente
  3. evitar el exceso de subclasificar para agregar responsabilidades adicionales .

inconvenientes:

  1. El uso excesivo de principio abierto cerrado (abierto para la extensión y cerrado para la modificación). Use esta característica con moderación, donde el código es menos probable que cambie.
  2. Demasiadas clases pequeñas y agregarán gastos generales de mantenimiento.

Un ejemplo del mundo real: Calcular el precio de la bebida, que puede contener múltiples sabores.

abstract class Beverage { 
    protected String name; 
    protected int price; 
    public Beverage(){ 

    } 
    public Beverage(String name){ 
     this.name = name; 
    } 
    public void setName(String name){ 
     this.name = name; 
    } 
    public String getName(){ 
     return name; 
    } 
    protected void setPrice(int price){ 
     this.price = price; 
    } 
    protected int getPrice(){ 
     return price; 
    } 
    protected abstract void decorateBeverage(); 

} 
class Tea extends Beverage{ 
    public Tea(String name){ 
     super(name); 
     setPrice(10); 
    } 
    public void decorateBeverage(){ 
     System.out.println("Cost of:"+ name +":"+ price); 
     // You can add some more functionality 
    } 
} 
class Coffee extends Beverage{ 
    public Coffee(String name){ 
     super(name); 
     setPrice(15); 
    } 
    public void decorateBeverage(){ 
     System.out.println("Cost of:"+ name +":"+ price); 
     // You can add some more functionality 
    } 
} 
abstract class BeverageDecorator extends Beverage { 
    protected Beverage beverage; 
    public BeverageDecorator(Beverage beverage){  
     this.beverage = beverage; 
     setName(beverage.getName()+"+"+getDecoratedName()); 
     setPrice(beverage.getPrice()+getIncrementPrice()); 
    } 
    public void decorateBeverage(){ 
     beverage.decorateBeverage(); 
     System.out.println("Cost of:"+getName()+":"+getPrice()); 
    } 
    public abstract int getIncrementPrice(); 
    public abstract String getDecoratedName(); 
} 
class SugarDecorator extends BeverageDecorator{ 
    public SugarDecorator(Beverage beverage){ 
     super(beverage); 
    } 
    public void decorateBeverage(){ 
     super.decorateBeverage(); 
     decorateSugar();   
    } 
    public void decorateSugar(){ 
     System.out.println("Added Sugar to:"+beverage.getName()); 
    } 
    public int getIncrementPrice(){ 
     return 5; 
    } 
    public String getDecoratedName(){ 
     return "Sugar"; 
    } 
} 
class LemonDecorator extends BeverageDecorator{ 
    public LemonDecorator(Beverage beverage){ 
     super(beverage); 
    } 
    public void decorateBeverage(){ 
     super.decorateBeverage(); 
     decorateLemon();  
    } 
    public void decorateLemon(){ 
     System.out.println("Added Lemon to:"+beverage.getName());  
    } 
    public int getIncrementPrice(){ 
     return 3; 
    } 
    public String getDecoratedName(){ 
     return "Lemon"; 
    } 
} 

public class VendingMachineDecorator { 
    public static void main(String args[]){ 
     Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea"))); 
     beverage.decorateBeverage(); 
     beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino"))); 
     beverage.decorateBeverage(); 
    } 
} 

de salida:

Cost of:Assam Tea:10 
Cost of:Assam Tea+Lemon:13 
Added Lemon to:Assam Tea 
Cost of:Assam Tea+Lemon+Sugar:18 
Added Sugar to:Assam Tea+Lemon 
Cost of:Cappuccino:15 
Cost of:Cappuccino+Lemon:18 
Added Lemon to:Cappuccino 
Cost of:Cappuccino+Lemon+Sugar:23 
Added Sugar to:Cappuccino+Lemon 

En este ejemplo se calcula el costo de bebidas en la máquina expendedora después de añadir muchos sabores a la bebida.

En el ejemplo anterior:

coste de la TEA = 10, limón y azúcar = 3 = 5. Si hacer Azúcar + limón + Té, cuesta 18.

Costo de café = 15, Limón = 3 y Sugar = 5. Si prepara Sugar + Lemon + Coffee, cuesta 23

Al usar el mismo Decorator para ambas bebidas (té y café), se ha reducido el número de subclases. En ausencia del patrón Decorator, debe tener diferentes subclases para diferentes combinaciones.

Las combinaciones serán así:

SugarLemonTea 
SugarTea 
LemonTea 

SugarLemonCapaccuino 
SugarCapaccuino 
LemonCapaccuino 

etc.

Mediante el uso mismo decorador para ambas bebidas, el número de sub-clases se han reducido. Es posible debido a composición en lugar de herencia concepto utilizado en este patrón. pregunta

SE relacionadas:

Decorator Pattern for IO

Enlaces de interés:

design-patterns-decorator por DZone

decorator por sourcemaking

oodesign artículo

Cuestiones relacionadas