decorator - pattern - Comprender el "Patrón Decorador" con un ejemplo del mundo real
facade pattern (11)
El patrón de decorador le permite agregar comportamiento de forma dinámica a los objetos.
Tomemos un ejemplo donde necesita construir una aplicación que calcule el precio de los diferentes tipos de hamburguesas. Debe manejar diferentes variaciones de hamburguesas, como "grande" o "con queso", cada una de las cuales tiene un precio relativo a la hamburguesa básica. Por ejemplo, agregue $ 10 por hamburguesa con queso, agregue $ 15 adicionales por una hamburguesa grande, etc.
En este caso, es posible que tengas la tentación de crear subclases para manejarlos. Podríamos expresar esto en Ruby como:
class Burger
def price
50
end
end
class BurgerWithCheese < Burger
def price
super + 15
end
end
En el ejemplo anterior, la clase BurgerWithCheese hereda de Burger y anula el método de precio para agregar $ 15 al precio definido en la súper clase. También crearía una clase LargeBurger y definiría el precio relativo a Burger. Pero también necesita definir una nueva clase para la combinación de "grande" y "con queso".
Ahora, ¿qué sucede si tenemos que servir "hamburguesas con papas fritas"? Ya tenemos 4 clases para manejar esas combinaciones, y necesitaremos agregar 4 más para manejar todas las combinaciones de las 3 propiedades: "grande", "con queso" y "con papas fritas". Necesitamos 8 clases ahora. Agregue otra propiedad y necesitaremos 16. Esto crecerá como 2 ^ n.
En cambio, intentemos definir un BurgerDecorator que tome un objeto Burger:
class BurgerDecorator
def initialize(burger)
self.burger = burger
end
end
class BurgerWithCheese < BurgerDecorator
def price
self.burger.price + 15
end
end
burger = Burger.new
cheese_burger = BurgerWithCheese.new(burger)
cheese_burger.price # => 65
En el ejemplo anterior, hemos creado una clase BurgerDecorator, de la cual la clase BurgerWithCheese hereda. También podemos representar la variación "grande" al crear la clase LargeBurger. Ahora podríamos definir una gran hamburguesa con queso en tiempo de ejecución como:
b = LargeBurger.new(cheese_burger)
b.price # => 50 + 15 + 20 = 85
¿Recuerda que el uso de la herencia para agregar la variación "con papas fritas" implicaría agregar 4 subclases más? Con los decoradores, simplemente creamos una nueva clase, BurgerWithFries, para manejar la nueva variación y manejar esto en tiempo de ejecución. Cada nueva propiedad necesitaría solo más decorador para cubrir todas las permutaciones.
PD. Esta es la versión corta de un artículo que escribí sobre el uso del patrón decorador en Ruby , que puede leer si desea encontrar ejemplos más detallados.
Estaba estudiando el patrón decorador como se documenta en GOF .
Por favor, ayúdame a entender el patrón decorador . ¿Podría alguien dar un ejemplo de caso de uso de dónde esto es útil en el mundo real?
El patrón decorador logra un objetivo único de agregar responsabilidades dinámicamente a cualquier objeto.
Considere un caso de una tienda de pizza. En la pizzería venderán algunas variedades de pizzas y también incluirán ingredientes en el menú. Ahora imagina una situación en la que si la pizzería tiene que ofrecer precios para cada combinación de pizza y cobertura. Incluso si hay cuatro pizzas básicas y 8 ingredientes diferentes, la aplicación se volvería loca manteniendo todas estas combinaciones concretas de pizzas y aderezos.
Aquí viene el patrón decorador.
Según el patrón del decorador, implementará los ingredientes ya que los decoradores y decoradoras decorarán las pizzas. Prácticamente, cada cliente querría ingredientes de su deseo y el monto final de la factura estará compuesto por las pizzas base y los aderezos ordenados adicionalmente. Cada decorador de tops sabrá sobre las pizzas que está decorando y su precio. El método GetPrice () del objeto Topping arrojará el precio acumulado tanto de la pizza como del topping.
EDITAR
Aquí hay un ejemplo de código de explicación anterior.
public abstract class BasePizza
{
protected double myPrice;
public virtual double GetPrice()
{
return this.myPrice;
}
}
public abstract class ToppingsDecorator : BasePizza
{
protected BasePizza pizza;
public ToppingsDecorator(BasePizza pizzaToDecorate)
{
this.pizza = pizzaToDecorate;
}
public override double GetPrice()
{
return (this.pizza.GetPrice() + this.myPrice);
}
}
class Program
{
[STAThread]
static void Main()
{
//Client-code
Margherita pizza = new Margherita();
Console.WriteLine("Plain Margherita: " + pizza.GetPrice().ToString());
ExtraCheeseTopping moreCheese = new ExtraCheeseTopping(pizza);
ExtraCheeseTopping someMoreCheese = new ExtraCheeseTopping(moreCheese);
Console.WriteLine("Plain Margherita with double extra cheese: " + someMoreCheese.GetPrice().ToString());
MushroomTopping moreMushroom = new MushroomTopping(someMoreCheese);
Console.WriteLine("Plain Margherita with double extra cheese with mushroom: " + moreMushroom.GetPrice().ToString());
JalapenoTopping moreJalapeno = new JalapenoTopping(moreMushroom);
Console.WriteLine("Plain Margherita with double extra cheese with mushroom with Jalapeno: " + moreJalapeno.GetPrice().ToString());
Console.ReadLine();
}
}
public class Margherita : BasePizza
{
public Margherita()
{
this.myPrice = 6.99;
}
}
public class Gourmet : BasePizza
{
public Gourmet()
{
this.myPrice = 7.49;
}
}
public class ExtraCheeseTopping : ToppingsDecorator
{
public ExtraCheeseTopping(BasePizza pizzaToDecorate)
: base(pizzaToDecorate)
{
this.myPrice = 0.99;
}
}
public class MushroomTopping : ToppingsDecorator
{
public MushroomTopping(BasePizza pizzaToDecorate)
: base(pizzaToDecorate)
{
this.myPrice = 1.49;
}
}
public class JalapenoTopping : ToppingsDecorator
{
public JalapenoTopping(BasePizza pizzaToDecorate)
: base(pizzaToDecorate)
{
this.myPrice = 1.49;
}
}
¿Qué es Decorator Design Pattern en Java?
La definición formal del patrón Decorator del libro de GoF (Patrones de diseño: elementos del software reutilizable orientado a objetos, 1995, Pearson Education, Inc. Publicación como Pearson Addison Wesley) dice que puede:
"Adjunte responsabilidades adicionales a un objeto dinámicamente. Los decoradores proporcionan una alternativa flexible a las subclases para ampliar la funcionalidad".
Digamos que tenemos una pizza y queremos decorarla con aderezos como pollo masala, cebolla y queso mozzarella. Veamos cómo implementarlo en Java ...
Programa para demostrar cómo implementar Decorator Design Pattern en Java.
- Ver más en: http://www.hubberspot.com/2013/06/decorator-design-pattern-in-java.html#sthash.zKj0xLrR.dpuf
Pizza.java:
<!-- language-all: lang-html -->
package com.hubberspot.designpattern.structural.decorator;
public class Pizza {
public Pizza() {
}
public String description(){
return "Pizza";
}
}
package com.hubberspot.designpattern.structural.decorator;
public abstract class PizzaToppings extends Pizza {
public abstract String description();
}
package com.hubberspot.designpattern.structural.decorator;
public class ChickenMasala extends PizzaToppings {
private Pizza pizza;
public ChickenMasala(Pizza pizza) {
this.pizza = pizza;
}
@Override
public String description() {
return pizza.description() + " with chicken masala, ";
}
}
package com.hubberspot.designpattern.structural.decorator;
public class MozzarellaCheese extends PizzaToppings {
private Pizza pizza;
public MozzarellaCheese(Pizza pizza) {
this.pizza = pizza;
}
@Override
public String description() {
return pizza.description() + "and mozzarella cheese.";
}
}
package com.hubberspot.designpattern.structural.decorator;
public class Onion extends PizzaToppings {
private Pizza pizza;
public Onion(Pizza pizza) {
this.pizza = pizza;
}
@Override
public String description() {
return pizza.description() + "onions, ";
}
}
package com.hubberspot.designpattern.structural.decorator;
public class TestDecorator {
public static void main(String[] args) {
Pizza pizza = new Pizza();
pizza = new ChickenMasala(pizza);
pizza = new Onion(pizza);
pizza = new MozzarellaCheese(pizza);
System.out.println("You''re getting " + pizza.description());
}
}
Ejemplo: escenario: supongamos que está escribiendo un módulo de cifrado. Esta encriptación puede encriptar el archivo claro usando DES - Data encryption standard. Del mismo modo, en un sistema puede tener el cifrado como AES - Estándar de cifrado avanzado. Además, puede tener la combinación de encriptación: First DES y AES. O puede tener primero AES, luego DES.
Discusión- ¿Cómo atenderás esta situación? No puede seguir creando el objeto de tales combinaciones, por ejemplo, AES y DES, con un total de 4 combinaciones. Por lo tanto, debe tener 4 objetos individuales. Esto se volverá complejo a medida que aumente el tipo de encriptación.
Solución: siga acumulando la pila, combinaciones según la necesidad, en tiempo de ejecución. Otra ventaja de este enfoque de pila es que puedes desenrollarlo fácilmente.
Aquí está la solución - en C ++.
En primer lugar, necesitas una clase base: una unidad fundamental de la pila. Puedes pensar como la base de la pila. En este ejemplo, es un archivo claro. Sigamos siempre el polimorfismo. Haga primero una clase de interfaz de esta unidad fundamental. De esta manera, puedes implementarlo como lo desees. Además, no necesita pensar en la dependencia al incluir esta unidad fundamental.
Aquí está la clase de interfaz -
class IclearData
{
public:
virtual std::string getData() = 0;
virtual ~IclearData() = 0;
};
IclearData::~IclearData()
{
std::cout<<"Destructor called of IclearData"<<std::endl;
}
Ahora, implementa esta clase de interfaz -
class clearData:public IclearData
{
private:
std::string m_data;
clearData();
void setData(std::string data)
{
m_data = data;
}
public:
std::string getData()
{
return m_data;
}
clearData(std::string data)
{
setData(data);
}
~clearData()
{
std::cout<<"Destructor of clear Data Invoked"<<std::endl;
}
};
Ahora, hagamos una clase abstracta de decorador, que puede ampliarse para crear cualquier tipo de sabores, aquí el sabor es el tipo de encriptación. Esta clase abstracta de decorador está relacionada con la clase base. Por lo tanto, el decorador "es un tipo de clase de interfaz". Por lo tanto, necesitas usar herencia.
class encryptionDecorator: public IclearData
{
protected:
IclearData *p_mclearData;
encryptionDecorator()
{
std::cout<<"Encryption Decorator Abstract class called"<<std::endl;
}
public:
std::string getData()
{
return p_mclearData->getData();
}
encryptionDecorator(IclearData *clearData)
{
p_mclearData = clearData;
}
virtual std::string showDecryptedData() = 0;
virtual ~encryptionDecorator() = 0;
};
encryptionDecorator::~encryptionDecorator()
{
std::cout<<"Encryption Decorator Destructor called"<<std::endl;
}
Ahora, hagamos una clase de decorador de concreto - Tipo de encriptación - AES -
const std::string aesEncrypt = "AES Encrypted ";
class aes: public encryptionDecorator
{
private:
std::string m_aesData;
aes();
public:
aes(IclearData *pClearData): m_aesData(aesEncrypt)
{
p_mclearData = pClearData;
m_aesData.append(p_mclearData->getData());
}
std::string getData()
{
return m_aesData;
}
std::string showDecryptedData(void)
{
m_aesData.erase(0,m_aesData.length());
return m_aesData;
}
};
Ahora, digamos que el tipo de decorador es DES -
const std :: string desEncrypt = "DES cifrado";
class des: public encryptionDecorator
{
private:
std::string m_desData;
des();
public:
des(IclearData *pClearData): m_desData(desEncrypt)
{
p_mclearData = pClearData;
m_desData.append(p_mclearData->getData());
}
std::string getData(void)
{
return m_desData;
}
std::string showDecryptedData(void)
{
m_desData.erase(0,desEncrypt.length());
return m_desData;
}
};
Hagamos un código de cliente para usar esta clase de decorador:
int main()
{
IclearData *pData = new clearData("HELLO_CLEAR_DATA");
std::cout<<pData->getData()<<std::endl;
encryptionDecorator *pAesData = new aes(pData);
std::cout<<pAesData->getData()<<std::endl;
encryptionDecorator *pDesData = new des(pAesData);
std::cout<<pDesData->getData()<<std::endl;
/** unwind the decorator stack ***/
std::cout<<pDesData->showDecryptedData()<<std::endl;
delete pDesData;
delete pAesData;
delete pData;
return 0;
}
Verás los siguientes resultados:
HELLO_CLEAR_DATA
Encryption Decorator Abstract class called
AES Encrypted HELLO_CLEAR_DATA
Encryption Decorator Abstract class called
DES Encrypted AES Encrypted HELLO_CLEAR_DATA
AES Encrypted HELLO_CLEAR_DATA
Encryption Decorator Destructor called
Destructor called of IclearData
Encryption Decorator Destructor called
Destructor called of IclearData
Destructor of clear Data Invoked
Destructor called of IclearData
Aquí está el diagrama UML: representación de clase de él. En caso, desea omitir el código y enfocarse en el aspecto del diseño.
El patrón Decorator le ayuda a cambiar o configurar una funcionalidad de su objeto mediante el encadenamiento con otras subclases similares de este objeto.
El mejor ejemplo serían las clases InputStream y OutputStream en el paquete java.io
File file=new File("target","test.txt");
FileOutputStream fos=new FileOutputStream(file);
BufferedOutputStream bos=new BufferedOutputStream(fos);
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.write(5);
oos.writeBoolean(true);
oos.writeBytes("decorator pattern was here.");
//... then close the streams of course.
Este es un ejemplo simple de agregar nuevo comportamiento dinámicamente a un objeto existente, o el patrón Decorator. Debido a la naturaleza de los lenguajes dinámicos como Javascript, este patrón se convierte en parte del lenguaje en sí.
// create a message object
var message = {
text: "Lorem ipsum dolor sit amet, consectetur adipisicing elit..."
};
// add logging behavior to the message object dynamically
message.log = function() {
console.log(this.text);
};
// use the newly added behavior to log text
message.log(); // Loren ipsum...
Hay un ejemplo en la Wikipedia sobre la decoración de una ventana con barra de desplazamiento:
http://en.wikipedia.org/wiki/Decorator_pattern
Aquí hay otro ejemplo muy "real" de "miembro del equipo, jefe de equipo y gerente", que ilustra que el patrón de decorador es irremplazable con una herencia simple:
https://zishanbilal.wordpress.com/2011/04/28/design-patterns-by-examples-decorator-pattern/
He usado el patrón Decorator extensivamente en mi trabajo. Hice una publicación en mi blog sobre cómo usarlo con el registro.
Vale la pena señalar que el modelo de Java i / o se basa en el patrón de decorador. Las capas de este lector encima de ese lector en la parte superior de ... es un ejemplo de decorador realmente real.
Patrón de diseño del decorador : este patrón ayuda a modificar las características de un objeto en tiempo de ejecución. Proporciona diferentes sabores a un objeto y le da flexibilidad para elegir qué ingredientes queremos usar en ese sabor.
Ejemplo de vida real: Digamos que tiene un asiento principal en la cabina en un vuelo. Ahora puedes elegir múltiples comodidades con el asiento. Cada amenidad tiene su propio costo asociado. Ahora, si un usuario elige Wifi y comida premium, se le cobrará por asiento + wifi + comida premium.
En este caso, el patrón de diseño del decorador realmente puede ayudarnos. Visite el enlace de arriba para entender más sobre el patrón de decorador y la implementación de un ejemplo de la vida real.
Decorador:
- Agregue comportamiento a objeto en tiempo de ejecución . La herencia es la clave para lograr esta funcionalidad, que es a la vez ventaja y desventaja de este patrón.
- Mejora el comportamiento de la interfaz.
- Decorator se puede ver como un compuesto degenerado con solo un componente. Sin embargo, un Decorador agrega responsabilidades adicionales, no está destinado a la agregación de objetos.
- La clase Decorator declara una relación de composición con la interfaz LCD (denominador de clase más bajo) y este miembro de datos se inicializa en su constructor.
- Decorator está diseñado para permitirle agregar responsabilidades a objetos sin subclasificar
Consulte el artículo de sourcemaking de sourcemaking para más detalles.
Decorador (Resumen) : es una clase / interfaz abstracta, que implementa la interfaz del componente. Contiene la interfaz de componente. En ausencia de esta clase, necesita muchas subclases de ConcreteDecorators para diferentes combinaciones. La composición del componente reduce las subclases innecesarias.
Ejemplo de JDK:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("a.txt")));
while(bis.available()>0)
{
char c = (char)bis.read();
System.out.println("Char: "+c);;
}
Eche un vistazo a la pregunta SE debajo para el diagrama UML y ejemplos de código.
Artículos útiles:
Ejemplo de palabra real del patrón Decorator: VendingMachineDecorator ha sido explicado @
Cuándo usar el patrón decorador?
Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
beverage.decorateBeverage();
beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
beverage.decorateBeverage();
En el ejemplo anterior, el té o el café (bebida) ha sido decorado por Sugar and Lemon.