Estaba estudiando el patrón de 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?
Estaba estudiando el patrón de 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?
Respuestas:
El patrón de decorador logra un único objetivo de agregar dinámicamente responsabilidades a cualquier objeto.
Considere un caso de una pizzería. En la pizzería venderán pocas variedades de pizza y también proporcionarán ingredientes en el menú. Ahora imagine una situación en la que la pizzería tiene que proporcionar precios para cada combinación de pizza y cobertura. Incluso si hay cuatro pizzas básicas y 8 coberturas diferentes, la aplicación se volvería loca manteniendo todas estas combinaciones concretas de pizzas y coberturas.
Aquí viene el patrón decorador.
Según el patrón del decorador, implementará coberturas como decoradores y las pizzas serán decoradas por los decoradores de esas coberturas. 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 ingredientes adicionales ordenados. Cada decorador de topping sabría acerca de las pizzas que está decorando y su precio. El método GetPrice () del objeto Topping devolvería el precio acumulado tanto de la pizza como del topping.
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;
}
}
Este es un ejemplo simple de agregar dinámicamente un nuevo comportamiento 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í.
// Person object that we will be decorating with logging capability
var person = {
name: "Foo",
city: "Bar"
};
// Function that serves as a decorator and dynamically adds the log method to a given object
function MakeLoggable(object) {
object.log = function(property) {
console.log(this[property]);
}
}
// Person is given the dynamic responsibility here
MakeLoggable(person);
// Using the newly added functionality
person.log('name');
switch
o un simple if
, podría afirmar que este es un gran ejemplo de agregar dinámicamente el comportamiento a una clase. PERO, necesitamos al menos dos clases para definir un decorador y objetos decorados en este patrón.
Vale la pena señalar que el modelo de E / S de Java se basa en el patrón decorador. La colocación en capas de este lector encima de ese lector encima de ... es un ejemplo de decorador del mundo real.
Ejemplo: escenario: supongamos que está escribiendo un módulo de cifrado. Este cifrado puede cifrar el archivo sin cifrar utilizando DES - Estándar de cifrado de datos. 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 cifrado: primero DES, luego AES. O puede tener primero AES, luego DES.
Discusión: ¿Cómo atenderá esta situación? No puede seguir creando el objeto de tales combinaciones, por ejemplo, AES y DES, un total de 4 combinaciones. Por lo tanto, debe tener 4 objetos individuales. Esto se volverá complejo a medida que aumente el tipo de cifrado.
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 puede desenrollarlo fácilmente.
Aquí está la solución: en C ++.
En primer lugar, necesita 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. Primero haga una clase de interfaz de esta unidad fundamental. De esta manera, puede implementarlo como desee. 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, implemente 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 se puede extender para crear cualquier tipo de sabores, aquí el sabor es el tipo de cifrado. 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, debe usar la 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 concreto - Tipo de cifrado - 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á 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 del mismo. En caso de que desee omitir el código y centrarse en el aspecto del diseño.
strategy pattern
?
El patrón de decorador lo ayuda a cambiar o configurar una funcionalidad de su objeto al encadenarlo con otras subclases similares de este objeto.
El mejor ejemplo sería 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.
Qué es el patrón de diseño de decorador en Java.
La definición formal del patrón Decorator del libro GoF (Patrones de diseño: Elementos de software orientado a objetos reutilizables, 1995, Pearson Education, Inc. Publicación como Pearson Addison Wesley) dice que puede,
"Asigne responsabilidades adicionales a un objeto dinámicamente. Los decoradores proporcionan una alternativa flexible a la subclasificación para ampliar la funcionalidad".
Digamos que tenemos una pizza y queremos decorarla con ingredientes como pollo Masala, cebolla y queso mozzarella. Veamos cómo implementarlo en Java ...
Programa para demostrar cómo implementar Decorator Design Pattern en Java.
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());
}
}
He usado el patrón Decorator ampliamente en mi trabajo. Hice una publicación en mi blog sobre cómo usarlo con el registro.
El patrón decorador le permite agregar dinámicamente comportamiento a los objetos.
Tomemos un ejemplo en el que necesita crear una aplicación que calcule el precio de 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 hamburguesa grande, etc.
En este caso, podría verse tentado a 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 superclase. También crearía una clase LargeBurger y definiría el precio relativo a Burger. Pero también debe definir una nueva clase para la combinación de "grande" y "con queso".
¿Qué sucede si necesitamos servir "hamburguesa 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 su lugar, 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 hereda la clase BurgerWithCheese. También podemos representar la variación "grande" creando la clase LargeBurger. Ahora podríamos definir una hamburguesa grande con queso en tiempo de ejecución como:
b = LargeBurger.new(cheese_burger)
b.price # => 50 + 15 + 20 = 85
¿Recuerda cómo usar la herencia para agregar la variación "con papas fritas" implicaría agregar 4 subclases más? Con los decoradores, solo crearíamos una nueva clase, BurgerWithFries, para manejar la nueva variación y manejar esto en tiempo de ejecución. Cada nueva propiedad necesitaría simplemente 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 obtener ejemplos más detallados.
Decorador:
Consulte la creación de fuentes artículo de para obtener más detalles.
Decorador (Resumen) : es una clase / interfaz abstracta, que implementa la interfaz del componente. Contiene interfaz de componentes. 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 siguiente pregunta SE para ver ejemplos de diagramas y códigos UML.
Artículos utiles:
Ejemplo de palabra real del patrón Decorator: VendingMachineDecorator se ha 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 con azúcar y limón.
El patrón de decorador logra un único objetivo de agregar dinámicamente responsabilidades a cualquier objeto .
Java I / O Model se basa en el patrón decorador.
Hay un ejemplo en 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, líder del equipo y gerente", que ilustra que el patrón del decorador es insustituible con una herencia simple:
https://zishanbilal.wordpress.com/2011/04/28/design-patterns-by-examples-decorator-pattern/
Hace algún tiempo había refactorizado una base de código para usar el patrón Decorator, así que intentaré explicar el caso de uso.
Supongamos que tenemos un conjunto de servicios y, en función de si el usuario ha adquirido una licencia de un servicio en particular, necesitamos iniciar el servicio.
Todos los servicios tienen una interfaz común.
interface Service {
String serviceId();
void init() throws Exception;
void start() throws Exception;
void stop() throws Exception;
}
abstract class ServiceSupport implements Service {
public ServiceSupport(String serviceId, LicenseManager licenseManager) {
// assign instance variables
}
@Override
public void init() throws Exception {
if (!licenseManager.isLicenseValid(serviceId)) {
throw new Exception("License not valid for service");
}
// Service initialization logic
}
}
Si observa con cuidado, ServiceSupport
depende de LicenseManager
. Pero, ¿por qué debería ser dependiente LicenseManager
? ¿Qué sucede si necesitáramos un servicio en segundo plano que no necesita verificar la información de la licencia? En la situación actual tendremos que entrenar de alguna manera LicenseManager
para volvertrue
a los servicios de fondo. Este enfoque no me pareció bien. Según mi verificación de licencia y otra lógica eran ortogonales entre sí.
Entonces, Decorator Pattern viene al rescate y aquí comienza a refactorizar con TDD.
class LicensedService implements Service {
private Service service;
public LicensedService(LicenseManager licenseManager, Service service) {
this.service = service;
}
@Override
public void init() {
if (!licenseManager.isLicenseValid(service.serviceId())) {
throw new Exception("License is invalid for service " + service.serviceId());
}
// Delegate init to decorated service
service.init();
}
// override other methods according to requirement
}
// Not concerned with licensing any more :)
abstract class ServiceSupport implements Service {
public ServiceSupport(String serviceId) {
// assign variables
}
@Override
public void init() {
// Service initialization logic
}
}
// The services which need license protection can be decorated with a Licensed service
Service aLicensedService = new LicensedService(new Service1("Service1"), licenseManager);
// Services which don't need license can be created without one and there is no need to pass license related information
Service aBackgroundService = new BackgroundService1("BG-1");
Tomemos ejemplo de PubG. Los rifles de asalto funcionan mejor con un zoom de 4x y mientras estamos en él, también necesitaríamos compensador y supresor. Reducirá el retroceso y el sonido de disparo, así como el eco. Tendremos que implementar esta función donde permitiremos a los jugadores comprar su arma favorita y sus accesorios. Los jugadores pueden comprar el arma o algunos de los accesorios o todos los accesorios y se les cobrará en consecuencia.
Veamos cómo se aplica el patrón decorador aquí:
Supongamos que alguien quiere comprar SCAR-L con los tres accesorios mencionados anteriormente.
Esto llevará a un diagrama de clase como este:
Ahora, podemos tener clases como esta:
public abstract class Gun {
private Double cost;
public Double getCost() {
return cost;
}
}
public abstract class GunAccessories extends Gun { }
public class Scarl extends Gun {
public Scarl() {
cost = 100;
}
}
public class Suppressor extends GunAccessories {
Gun gun;
public Suppressor(Gun gun) {
cost = 5;
this.gun = gun;
}
public double getCost(){
return cost + gun.getCost();
}
}
public class GunShop{
public static void main(String args[]){
Gun scarl = new Scarl();
scarl = new Supressor(scarl);
System.out.println("Price is "+scarl.getCost());
}
}
También podemos agregar otros accesorios y decorar nuestra pistola.
Referencia:
https://nulpointerexception.com/2019/05/05/a-beginner-guide-to-decorator-pattern/
Patrón de diseño de 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 brinda flexibilidad para elegir qué ingredientes queremos usar en ese sabor.
Ejemplo de la vida real: Digamos que tiene un asiento de cabina principal en un vuelo. Ahora puede 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 anterior para comprender más sobre el patrón decorador y la implementación de un ejemplo de la vida real.