design patterns - patron - Cuándo usar el patrón decorador?
patron proxy (7)
- para agregar responsabilidades a objetos individuales de forma dinámica y transparente.
- por responsabilidades que pueden ser retiradas.
- 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.
Repaso 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.
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 mejor / óptima / elegante solución. Situaciones específicas donde la necesidad del patrón de decorador es realmente útil.
Gracias.
El decorador es simple pero extremadamente poderoso. Es clave para lograr la separación de preocupaciones y es una herramienta esencial para el Principio Abierto Cerrado. Tome un ejemplo común de hacer un pedido de un producto:
IOrderGateway
{
void PlaceOrder(Order order);
{
Implementación principal: AmazonAffiliateOrderGateway
Los posibles decoradores podrían ser:
-
IncrementPerformanceCounterOrderGateway
-
QueueOrderForLaterOnTimeoutOrderGateway
-
EmailOnExceptionOrderGateway
-
InterceptTestOrderAndLogOrderGateway
Echa un vistazo here para ver un ejemplo más detallado.
El patrón de decorador se usa mucho con las transmisiones: puede envolver una secuencia con una secuencia para obtener una funcionalidad adicional. 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.
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 rastrea los números de línea. La línea 1 crea un lector de archivos ( frdr
) y la línea 2 agrega el seguimiento de números de línea.
En realidad, le recomiendo encarecidamente que consulte el código fuente de Java para las clases de E / S de Java.
Recientemente utilicé el patrón de decorador en un servicio web que usa la siguiente interfaz CommandProcessor:
public Command receive(Request request);
public Response execute(Command command);
public void respond(Response response);
Básicamente, CommandProcessor recibe una Solicitud y crea el Comando correcto, ejecuta el Comando y crea la Respuesta adecuada, 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
}
}
Entonces, el verdadero CommandProcessor está incluido dentro de TimerDecorator, y puedo tratar TimerDecorator como el CommandProcessor de destino, pero ahora se ha agregado la lógica de tiempo.
Zend Framework usa el decorador para los elementos del formulario
Más información: http://framework.zend.com/manual/en/zend.form.decorators.html
Decorator patrón Decorator 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:
- Agregar funcionalidades / responsabilidades adicionales dinámicamente
- Eliminar funcionalidades / responsabilidades dinámicamente
- Evite demasiada subclasificación para agregar responsabilidades adicionales.
Inconvenientes:
- Uso excesivo del principio de Open Closed (Abierto para extensión y Cerrado para modificación). Use esta característica con moderación, donde el código es menos probable que cambie.
- Demasiadas clases pequeñas y agregarán gastos generales de mantenimiento .
Un ejemplo del mundo real: calcule el precio de la bebida, que puede contener sabores múltiples.
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();
}
}
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
Este ejemplo calcula el costo de la bebida en la máquina expendedora después de agregar muchos sabores a la bebida.
En el ejemplo anterior:
Costo de té = 10, limón = 3 y azúcar = 5. Si usted hace azúcar + té de limón +, cuesta 18.
Costo del café = 15, limón = 3 y azúcar = 5. Si usted hace azúcar + limón + café, cuesta 23
Al usar el mismo decorador para ambas bebidas (té y café), se redujo 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.
Al usar el mismo decorador para ambas bebidas, se redujo el número de subclases. Es posible debido a la composición en lugar del concepto de herencia utilizado en este patrón.
Pregunta SE relacionada:
Enlaces útiles:
design-patterns-decorator por dzone
decorator por sourcemaking
oodesign article