Patrón decorador

Ajustar Compartir Imprimir Citar
Patrón de diseño en programación orientada al objeto

En la programación orientada a objetos, el patrón decorador es un patrón de diseño que permite agregar comportamiento a un objeto individual, de forma dinámica, sin afectar el comportamiento de otros objetos de la misma clase. El patrón decorador suele ser útil para adherirse al principio de responsabilidad única, ya que permite que la funcionalidad se divida entre clases con áreas de interés únicas, así como al principio abierto-cerrado, al permitir que la funcionalidad de una clase se amplíe sin que se vea afectada. modificado. El uso del decorador puede ser más eficiente que la creación de subclases, porque el comportamiento de un objeto se puede aumentar sin definir un objeto completamente nuevo.

Resumen

El patrón de diseño decorator es uno de los veintitrés patrones de diseño conocidos; estos describen cómo resolver problemas de diseño recurrentes y diseñar software orientado a objetos flexible y reutilizable, es decir, objetos que son más fáciles de implementar, cambiar, probar y reutilizar.

¿Qué problemas puede resolver?

Al usar subclases, diferentes subclases amplían una clase de diferentes maneras. Pero una extensión está vinculada a la clase en tiempo de compilación y no se puede cambiar en tiempo de ejecución.

¿Qué solución describe?

Definir objetos Decorator que

Esto permite trabajar con diferentes objetos Decorator para extender la funcionalidad de un objeto dinámicamente en tiempo de ejecución.
Vea también la clase UML y el diagrama de secuencia a continuación.

Intención

Decorador diagrama de clase UML

El patrón decorador se puede usar 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 el momento del diseño. Esto se logra diseñando una nueva clase Decorador que envuelve la clase original. Este envoltorio podría lograrse mediante la siguiente secuencia de pasos:

  1. Subclase el original Componente clase en una Decorador clase (ver diagrama UML);
  2. En el Decorador clase, agrega un Componente puntero como campo;
  3. En el Decorador clase, pase una Componente a la Decorador constructor para inicializar Componente puntero;
  4. En el Decorador clase, adelante todo Componente métodos para Componente puntero; y
  5. En la clase ConcreteDecorator, anule cualquier Componente método(s) cuyo comportamiento debe ser modificado.

Este patrón está diseñado para que varios decoradores se puedan apilar uno encima del otro, cada vez que se agrega una nueva funcionalidad a los métodos anulados.

Tenga en cuenta que los decoradores y el objeto de clase original comparten un conjunto común de características. En el diagrama anterior, el método operation() estaba disponible tanto en la versión decorada como en la no decorada.

Las funciones de decoración (por ejemplo, métodos, propiedades u otros miembros) generalmente se definen mediante una interfaz, mezcla (también conocida como rasgo) o herencia de clase que comparten los decoradores y el objeto decorado. En el ejemplo anterior, la clase Component es heredada tanto por ConcreteComponent como por las subclases que descienden de Decorator.

El patrón decorador es una alternativa a las subclases. La creación de subclases agrega comportamiento en tiempo de compilación y el cambio afecta a todas las instancias de la clase original; La decoración puede proporcionar un nuevo comportamiento en tiempo de ejecución para los objetos seleccionados.

Esta diferencia se vuelve más importante cuando hay varias formas independientes de extender la funcionalidad. En algunos lenguajes de programación orientados a objetos, las clases no se pueden crear en tiempo de ejecución y, por lo general, no es posible predecir, en tiempo de diseño, qué combinaciones de extensiones se necesitarán. Esto significaría que tendría que crearse una nueva clase para cada combinación posible. Por el contrario, los decoradores son objetos creados en tiempo de ejecución y se pueden combinar según el uso. Las implementaciones de I/O Streams de Java y.NET Framework incorporan el patrón decorador.

Motivación

diagrama UML para el ejemplo de ventana

Como ejemplo, considere una ventana en un sistema de ventanas. Para permitir el desplazamiento del contenido de la ventana, es posible que desee agregarle barras de desplazamiento horizontales o verticales, según corresponda. Suponga que las ventanas están representadas por instancias de la interfaz Ventana y suponga que esta clase no tiene funcionalidad para agregar barras de desplazamiento. Se podría crear una subclase ScrollingWindow que los proporcione, o crear un ScrollingWindowDecorator que agregue esta funcionalidad a los objetos Window existentes. En este punto, cualquier solución estaría bien.

Ahora, suponga que también desea la capacidad de agregar bordes a las ventanas. De nuevo, la clase Ventana original no tiene soporte. La subclase ScrollingWindow ahora plantea un problema, porque efectivamente ha creado un nuevo tipo de ventana. Si desea agregar soporte de borde a muchas pero no a todas ventanas, debe crear subclases WindowWithBorder y ScrollingWindowWithBorder, etc. Este problema empeora con cada nueva función o subtipo de ventana que se agregará. Para la solución del decorador, se crea un nuevo BorderedWindowDecorator. Cualquier combinación de ScrollingWindowDecorator o BorderedWindowDecorator puede decorar ventanas existentes. Si es necesario agregar la funcionalidad a todas las ventanas, la clase base se puede modificar. Por otro lado, a veces (por ejemplo, usando marcos externos) no es posible, legal o conveniente modificar la clase base.

En el ejemplo anterior, las clases SimpleWindow y WindowDecorator implementan la interfaz Window, que define el draw() y el método getDescription() que se requieren en este escenario para decorar un control de ventana.

Casos de uso comunes

Aplicación de decoradores

Agregar o quitar decoradores a pedido (como presionar un botón) es un patrón de interfaz de usuario común, a menudo implementado junto con el patrón de diseño de comando. Por ejemplo, una aplicación de edición de texto puede tener un botón para resaltar texto. Al presionar el botón, los glifos de texto individuales actualmente seleccionados se envolverán en decoradores que modifican su función dibujar (), lo que hace que se dibujen de manera resaltada (una implementación real probablemente también usaría un sistema de demarcación para maximizar la eficiencia).

Aplicar o quitar decoradores en función de los cambios de estado es otro caso de uso común. Dependiendo del alcance del estado, los decoradores se pueden aplicar o quitar a granel. De manera similar, el patrón de diseño de estado se puede implementar utilizando decoradores en lugar de objetos subclasificados que encapsulan la funcionalidad cambiante. El uso de decoradores de esta manera hace que el estado interno y la funcionalidad del objeto de estado sean más compositivos y capaces de manejar una complejidad arbitraria.

Uso en objetos Flyweight

La decoración también se usa a menudo en el patrón de diseño Flyweight. Los objetos de peso mosca se dividen en dos componentes: un componente invariable que se comparte entre todos los objetos de peso mosca; y un componente decorado variante que puede compartirse parcialmente o no compartirse por completo. Esta partición del objeto flyweight tiene como objetivo reducir el consumo de memoria. Los decoradores normalmente también se almacenan en caché y se reutilizan. Todos los decoradores contendrán una referencia común al objeto invariable compartido. Si el estado decorado es solo parcialmente variable, los decoradores también se pueden compartir hasta cierto punto, aunque se debe tener cuidado de no alterar su estado mientras se usan. UITableView de iOS implementa el patrón de peso ligero de esta manera: las celdas reutilizables de una vista de tabla son decoradores que contienen referencias a un objeto de fila de vista de tabla común, y las celdas se almacenan en caché/reutilizan.

Obstáculos de interactuar con decoradores

La aplicación de combinaciones de decoradores de diversas maneras a una colección de objetos presenta algunos problemas al interactuar con la colección de una manera que aprovecha al máximo la funcionalidad agregada por los decoradores. El uso de patrones de Adaptador o Visitante puede ser útil en tales casos. La interfaz con múltiples capas de decoradores plantea desafíos adicionales y la lógica de los adaptadores y visitantes debe diseñarse para tener en cuenta eso.

Relevancia arquitectónica

Los decoradores admiten un enfoque jerárquico de composición en lugar de de arriba hacia abajo para ampliar la funcionalidad. Un decorador hace posible agregar o modificar el comportamiento de una interfaz en tiempo de ejecución. Se pueden usar para envolver objetos en una combinación arbitraria de varias capas. Hacer lo mismo con las subclases significa implementar redes complejas de herencia múltiple, lo cual es ineficiente para la memoria y, en cierto punto, simplemente no puede escalar. Del mismo modo, intentar implementar la misma funcionalidad con propiedades infla cada instancia del objeto con propiedades innecesarias. Por las razones anteriores, los decoradores a menudo se consideran una alternativa eficiente en memoria a la subclasificación.

Los decoradores también se pueden usar para especializar objetos que no son subclasificables, cuyas características deben modificarse en tiempo de ejecución (como se mencionó en otra parte) o, en general, objetos que carecen de alguna funcionalidad necesaria.

Uso para mejorar las API

El patrón Decorator también puede aumentar el patrón Facade. Una fachada está diseñada para interactuar simplemente con el sistema complejo que encapsula, pero no agrega funcionalidad al sistema. Sin embargo, el envoltorio de un sistema complejo proporciona un espacio que puede utilizarse para introducir una nueva funcionalidad basada en la coordinación de los subcomponentes del sistema. Por ejemplo, un patrón de fachada puede unificar muchos diccionarios de diferentes idiomas bajo una interfaz de diccionario multilingüe. La nueva interfaz también puede proporcionar nuevas funciones para traducir palabras entre idiomas. Este es un patrón híbrido: la interfaz unificada proporciona un espacio para el aumento. Piense en los decoradores que no se limitan a envolver objetos individuales, sino que también son capaces de envolver grupos de objetos en este enfoque híbrido.

Alternativas a las decoradoras

(feminine)

Como alternativa al patrón decorador, el adaptador se puede usar cuando el envoltorio debe respetar una interfaz particular y debe admitir un comportamiento polimórfico, y la Fachada cuando se desea una interfaz más fácil o más simple para un objeto subyacente.

PatrónIntent
AdaptadorConvierte una interfaz a otra para que coincida con lo que el cliente espera
DecoradorAgrega dinámicamente la responsabilidad a la interfaz envolviendo el código original
FacadeProporciona una interfaz simplificada

Estructura

Clase UML y diagrama de secuencia

Un diagrama de clase y secuencia UML muestra para el patrón de diseño Decorator.

En el diagrama de clases UML anterior, la clase abstracta Decorator mantiene una referencia (component) al objeto decorado (Component) y le reenvía todas las solicitudes (componente.operación()). Esto hace que Decorator sea transparente (invisible) para los clientes de Component.

Las subclases (Decorator1, Decorator2) implementan un comportamiento adicional (addBehavior()) que debe agregarse al Component (antes/después de enviarle una solicitud).
El diagrama de secuencia muestra las interacciones en tiempo de ejecución: El objeto Client funciona a través de los objetos Decorator1 y Decorator2 para ampliar la funcionalidad de un objeto Component1.
El Client llama a operation() en Decorator1, que reenvía la solicitud a Decorator2. Decorator2 realiza addBehavior() después de reenviar la solicitud a Component1 y vuelve a Decorator1, que realiza addBehavior() y vuelve al Cliente.

Ejemplos

C++

Esta implementación de C++14 se basa en la implementación anterior a C++98 del libro.

#include ■iostream#include - No.// define la interfaz para objetos que pueden tener responsabilidades agregadas a ellos dinámicamente.clase VisualComponente {} // Componentepúblico: virtual vacío dibujo() = 0; virtual ~VisualComponente() = por defecto;};// define un objeto al que pueden adjuntarse responsabilidades adicionales.clase TextView : público VisualComponente {} // ConcreteComponentepúblico: virtual vacío dibujo() {} std::Cout .. "TextView::draw"; }};// mantiene una referencia a un objeto Componente y define una interfaz que se ajusta a la interfaz de Componente.clase Decorador : público VisualComponente {}público: Decorador() : componente()nullptr) {} Decorador()VisualComponente* componente_) : componente()componente_) {} Decorador()const Decorador") = Borrador; // regla de tres Decorador" operador=()const Decorador") = Borrador; virtual vacío dibujo() {} componente-dibujo(); }protegida: VisualComponente* componente;};// añade responsabilidades al componente.clase BorderDecorator : público Decorador {} // ConcreteDecoratorpúblico: BorderDecorator()std::shared_ptr.VisualComponente componente_, int ancho_) : ancho()ancho_) {} componente = componente_.#(); } virtual vacío dibujo() {} Decorador::dibujo(); Dibujo()ancho); }privado: vacío Dibujo()int ancho_) {} std::Cout .. "BorderDecorator::drawBorder ancho=" .. ancho_ .. '; } int ancho;};clase Ventana {}público: vacío setContents ()std::shared_ptr.VisualComponente contents_) {} contenidos = contents_.#(); } vacío dibujo () {} contenidos-dibujo(); std::Cout .. 'n '; }privado: VisualComponente* contenidos;};int principal() {} // Los punteros inteligentes evitan las fugas de memoria. std::único_ptr.Ventana ventana = std::make_unique.Ventana(); std::shared_ptr.TextView texto Ver = std::make_shared.TextView(); ventana-setContents()texto Ver); ventana-dibujo(); std::shared_ptr.BorderDecorator bd1 = std::make_shared.BorderDecorator ()texto Ver, 1); ventana-setContents()bd1); ventana-dibujo(); std::shared_ptr.BorderDecorator bd2 = std::make_shared.BorderDecorator ()texto Ver, 2); std::shared_ptr.BorderDecorator bd3 = std::make_shared.BorderDecorator ()bd2, 1); ventana-setContents()bd3); ventana-dibujo();}

La salida del programa es como

TextView::dibujo TextView::dibujo BorderDecorator::Dibujo ancho=1 TextView::dibujo BorderDecorator::Dibujo ancho=2 BorderDecorator::Dibujo ancho=1

Ir

paquete decologimportación ()"log""tiempo")//Operada Fn representa operaciones que requieren decoraciónTipo OperateFn func()//Decorar la operaciónfunc Decorar()opFn OperateFn) {}aplazamiento func()s tiempo.Hora) {}log.Printf()"tiempo elegido %0,2d ms", tiempo.Desde()s).Nanoseconds() / 1000000).tiempo.Ahora())// función de operación realopFn()}// paquete principalpaquete principalimportación ()"github.com/tkstorm/go-design/structural/decorator/decolog""log""Math/rand""tiempo")//output://2019/08/19 19:05:24 terminar acción un//2019/08/19 19:05:24 tiempo transcurrido 77 ms//2019/08/19 19:05:24 terminar acción b//2019/08/19 19:05:24 tiempo transcurrido 88 msfunc principal() {}// decorar el tronco adecolog.Decorar()decolog.OperateFn()DoActionA)// decorar tronco bdecolog.Decorar()decolog.OperateFn()DoActionB)}func DoActionA() {}tiempo.Duerme()tiempo.Duración()rand.Intn()200) * tiempo.Millisecond)log.Println()"Acción definitiva a")}func DoActionB() {}tiempo.Duerme()tiempo.Duración()rand.Intn()200) * tiempo.Millisecond)log.Println()"Acción definitiva b")}

C++

Aquí se presentan dos opciones: primero, un decorador dinámico que se puede componer en tiempo de ejecución (tiene problemas para llamar a funciones decoradas a menos que se utilice un proxy de forma explícita) y un decorador que usa la herencia mixta.

Decorador dinámico

#include ■iostream#include Identificadostruct Forma {} virtual ~Forma() = por defecto; virtual std::cuerda GetName() const = 0;};struct Circle : Forma {} vacío Resize()flotador factor) {} radio *= factor; } std::cuerda GetName() const Anulación {} retorno std::cuerda()"Un círculo de radio") + std::to_string()radio); } flotador radio = 10.0f;};struct ColoredShape : Forma {} ColoredShape()const std::cuerda" color, Forma* forma) : color()color), forma()forma) {} std::cuerda GetName() const Anulación {} retorno forma-GetName() + "que es de color" + color; } std::cuerda color; Forma* forma;};int principal() {} Circle círculo; ColoredShape color_shape()"rojo", "círculo); std::Cout .. color_shape.GetName() .. std::endl;}
#include - No.#include ■iostream#include Identificadostruct WebPage{} virtual vacío pantalla()=0; virtual ~WebPage() = por defecto;};struct BasicWebPage : WebPage{} std::cuerda html; vacío pantalla() Anulación {} std::Cout .. "página web básica" .. std::endl; }};struct WebPageDecorator : WebPage{} WebPageDecorator()std::único_ptr.WebPage webPage): _webPage()std::Muévanse.()webPage) {} } vacío pantalla() Anulación {} _webPage-pantalla(); }privado: std::único_ptr.WebPage _webPage;};struct AuthenticatedWebPage : WebPageDecorator{} AuthenticatedWebPage()std::único_ptr.WebPage webPage):  WebPageDecorator()std::Muévanse.()webPage) {} vacío autenticación Usuario() {} std::Cout .. "autentificación hecha" .. std::endl; } vacío pantalla() Anulación {} autenticación Usuario(); WebPageDecorator::pantalla(); }};struct AutorizadoWebPage : WebPageDecorator{} AutorizadoWebPage()std::único_ptr.WebPage webPage):  WebPageDecorator()std::Muévanse.()webPage) {} vacío autorizado Usuario() {} std::Cout .. "hecho autorizado" .. std::endl; } vacío pantalla() Anulación {} autorizado Usuario(); WebPageDecorator::pantalla(); }};int principal()int argc, char* argv[]){} std::único_ptr.WebPage myPage = std::make_unique.BasicWebPage(); myPage = std::make_unique.AutorizadoWebPage()std::Muévanse.()myPage)); myPage = std::make_unique.AuthenticatedWebPage()std::Muévanse.()myPage)); myPage-pantalla(); std::Cout .. std::endl; retorno 0;}

Decorador estático (herencia mixta)

Este ejemplo muestra una implementación estática de Decorator, que es posible gracias a la capacidad de C++ para heredar del argumento de la plantilla.

#include ■iostream#include Identificadostruct Circle {} vacío Resize()flotador factor) {} radio *= factor; } std::cuerda GetName() const {} retorno std::cuerda()"Un círculo de radio") + std::to_string()radio); } flotador radio = 10.0f;};plantilla .nombre Tstruct ColoredShape : público T {} ColoredShape()const std::cuerda" color) : color()color) {} std::cuerda GetName() const {} retorno T::GetName() + "que es de color" + color; } std::cuerda color;};int principal() {} ColoredShape.Circle red_circle()"rojo"); std::Cout .. red_circle.GetName() .. std::endl; red_circle.Resize()1,5f); std::Cout .. red_circle.GetName() .. std::endl;}

Java

Primer ejemplo (ventana/escenario de desplazamiento)

El siguiente ejemplo de Java ilustra el uso de decoradores utilizando el escenario de ventana/desplazamiento.

// La clase de interfaz de ventanapúblico interfaz Ventana {} vacío dibujo(); // Dibuja la ventana String getDescription(); // Devuelve una descripción de la ventana}// Implementación de una ventana sencilla sin barras de desplazamientoclase SimpleWindow implementos Ventana {} @Override público vacío dibujo() {} // Ventana de dibujo } @Override público String getDescription() {} retorno "simple ventana"; }}

Las siguientes clases contienen los decoradores para todas las clases Window, incluidas las propias clases de decoradores.

// clase de decorador abstracto - nota que implementa Ventanaabstracto clase WindowDecorator implementos Ventana {} privado final Ventana windowToBeDecorated; // la ventana que está decorada público WindowDecorator ()Ventana windowToBeDecorated) {} esto.windowToBeDecorated = windowToBeDecorated; } @Override público vacío dibujo() {} windowToBeDecorated.dibujo(); //Delegación } @Override público String getDescription() {} retorno windowToBeDecorated.getDescription(); //Delegación }}// El primer decorador de hormigón que añade funcionalidad vertical de barras de desplazamientoclase VerticalScrollBarDecorator extensiones WindowDecorator {} público VerticalScrollBarDecorator ()Ventana windowToBeDecorated) {} super()windowToBeDecorated); } @Override público vacío dibujo() {} super.dibujo(); dibujoVerticalScrollBar(); } privado vacío dibujoVerticalScrollBar() {} // Dibuja la barra de desplazamiento vertical } @Override público String getDescription() {} retorno super.getDescription() + ", incluyendo barras verticales de desplazamiento"; }}// El segundo decorador de hormigón que añade funcionalidad de barra de desplazamiento horizontalclase HorizontalScrollBarDecorator extensiones WindowDecorator {} público HorizontalScrollBarDecorator ()Ventana windowToBeDecorated) {} super()windowToBeDecorated); } @Override público vacío dibujo() {} super.dibujo(); barra de tracciónHorizontalScrollBar(); } privado vacío barra de tracciónHorizontalScrollBar() {} // Dibujar la barra de desplazamiento horizontal } @Override público String getDescription() {} retorno super.getDescription() + ", incluyendo barras de desplazamiento horizontales"; }}

Aquí hay un programa de prueba que crea una instancia de Window que está completamente decorada (es decir, con barras de desplazamiento verticales y horizontales) e imprime su descripción:

público clase DecoradaWindow Prueba {} público estática vacío principal()String[] args) {} // Crear una ventana decorada con barras de desplazamiento horizontales y verticales Ventana decorada Ventana = nuevo HorizontalScrollBarDecorator () nuevo VerticalScrollBarDecorator ()nuevo SimpleWindow())); // Imprima la descripción de la ventana Sistema.Fuera..println()decorada Ventana.getDescription()); }}

La salida de este programa es "ventana simple, incluidas las barras de desplazamiento verticales, incluidas las barras de desplazamiento horizontales". Observe cómo el método getDescription de los dos decoradores primero recupera la descripción de la Ventana decorada y la decora con un sufijo.

A continuación se muestra la clase de prueba JUnit para el desarrollo basado en pruebas

importación estática org.junit.Assert.assertEquals;importación Org.junit. Prueba;público clase WindowDecorator Prueba {}@Testpúblico vacío testWindowDecorator Prueba() {} Ventana decorada Ventana = nuevo HorizontalScrollBarDecorator()nuevo VerticalScrollBarDecorator()nuevo SimpleWindow())); // afirmar que la descripción incluye de hecho barras horizontales + verticales assertEquals()"ventana simple, incluyendo barras verticales, incluyendo barras de desplazamiento horizontales", decorada Ventana.getDescription());}}


Segundo ejemplo (escenario de preparación de café)

El siguiente ejemplo de Java ilustra el uso de decoradores en el escenario de preparación de café. En este ejemplo, el escenario solo incluye costos e ingredientes.

// La interfaz Café define la funcionalidad del Café implementado por decoradorpúblico interfaz Café {} público doble # Costo(); // Devuelve el costo del café público String getIngredients(); // Devuelve los ingredientes del café}// Ampliación de un café simple sin ningún ingrediente extrapúblico clase SimpleCoffee implementos Café {} @Override público doble # Costo() {} retorno 1; } @Override público String getIngredients() {} retorno "Coffee"; }}

Las siguientes clases contienen los decoradores para todas las clases de Coffee, incluidas las propias clases de decoradores.

// Clase de decoración abstracta - note que implementa Interfaz de cafépúblico abstracto clase CoffeeDecorator implementos Café {} privado final Café decoradoCoffee; público CoffeeDecorator()Café c) {} esto.decoradoCoffee = c; } @Override público doble # Costo() {} // Aplicación de métodos de la interfaz retorno decoradoCoffee.# Costo(); } @Override público String getIngredients() {} retorno decoradoCoffee.getIngredients(); }}// Decorador ConMilk mezcla leche en café.// Nota que extiende CoffeeDecorator.clase ConMilk extensiones CoffeeDecorator {} público ConMilk()Café c) {} super()c); } @Override público doble # Costo() {} // Métodos dominantes definidos en la superclase abstracta retorno super.# Costo() + 0.5; } @Override público String getIngredients() {} retorno super.getIngredients() + ", Milk"; }}// Decorador ConSprinkles mezcla las rocias sobre el café.// Nota que extiende CoffeeDecorator.clase ConSprinkles extensiones CoffeeDecorator {} público ConSprinkles()Café c) {} super()c); } @Override público doble # Costo() {} retorno super.# Costo() + 0.2; } @Override público String getIngredients() {} retorno super.getIngredients() + ", espolvoritos"; }}

Aquí hay un programa de prueba que crea una instancia de Café que está completamente decorada (con leche y chispas), y calcula el costo del café e imprime sus ingredientes:

público clase Main {} público estática vacío impresión Info()Café c) {} Sistema.Fuera..println()"Cost: " + c.# Costo() + "; Ingredientes: " + c.getIngredients()); } público estática vacío principal()String[] args) {} Café c = nuevo SimpleCoffee(); impresión Info()c); c = nuevo ConMilk()c); impresión Info()c); c = nuevo ConSprinkles()c); impresión Info()c); }}

El resultado de este programa se muestra a continuación:

Costo: 1.0; Ingredientes: Café
Costo: 1,5; Ingredientes: Café, Leche
Costo: 1.7; Ingredientes: Café, Leche, Espolvoreas

PHP

abstracto clase Componente{} protegida $data; protegida $value; abstracto público función # Datos(); abstracto público función # Valor();}clase ConcreteComponente extensiones Componente{} público función __construir() {} Esto-valor = 1000; Esto-datos = "Concrete Component:t{}Esto-valor}n"; } público función # Datos() {} retorno Esto-datos; } público función # Valor() {} retorno Esto-valor; }}abstracto clase Decorador extensiones Componente{}}clase ConcreteDecorator1 extensiones Decorador{} público función __construir()Componente $data) {} Esto-valor = 500; Esto-datos = $data; } público función # Datos() {} retorno Esto-datos-# Datos() . "Decorador de hormigón 1:t{}Esto-valor}n"; } público función # Valor() {} retorno Esto-valor + Esto-datos-# Valor(); }}clase ConcreteDecorator2 extensiones Decorador{} público función __construir()Componente $data) {} Esto-valor = 500; Esto-datos = $data; } público función # Datos() {} retorno Esto-datos-# Datos() . "Decorador de hormigón 2:t{}Esto-valor}n"; } público función # Valor() {} retorno Esto-valor + Esto-datos-# Valor(); }}clase Cliente{} privado $componente; público función __construir() {} Esto-componente = nuevo ConcreteComponente(); Esto-componente = Esto-envoltorioComponente()Esto-componente); eco Esto-componente-# Datos(); eco "Client:ttt"; eco Esto-componente-# Valor(); } privado función envoltorioComponente()Componente $componente) {} $componente1 = nuevo ConcreteDecorator1()$componente); $componente2 = nuevo ConcreteDecorator2()$componente1); retorno $componente2; }}$client = nuevo Cliente();// Resultado: #quanton81// Componente de hormigón: 1000//Decorador de hormigón 1: 500//Decorador de hormigón 2: 500//Client: 2000

Pitón

El siguiente ejemplo de Python, tomado de Python Wiki - DecoratorPattern, nos muestra cómo canalizar decoradores para agregar dinámicamente muchos comportamientos en un objeto:

"Decoradores demostrados en un mundo de 10x10 rejilla de valores 0-255. "importación al azardef s32_to_u16()x): si x . 0: Signatura = 0xF000 más: Signatura = 0 inferior = x " 0x00007FFF retorno inferior Silencio Signaturadef seed_from_xy()x, Sí.): retorno s32_to_u16()x) Silencio ()s32_to_u16()Sí.) .. 16)clase RandomSquare: def __init_()s, seed_modifier): s.seed_modifier = seed_modifier def #()s, x, Sí.): semillas = seed_from_xy()x, Sí.) ^ s.seed_modifier al azar.semillas()semillas) retorno al azar.Randy.()0, 255)clase DataSquare: def __init_()s, inicial_valor=Ninguno): s.datos = [inicial_valor] * 10 * 10 def #()s, x, Sí.): retorno s.datos[Sí. * 10) + x] # sí: estos son todos 10x10 def set()s, x, Sí., u): s.datos[Sí. * 10) + x] = uclase CacheDecorator: def __init_()s, decorada): s.decorada = decorada s.cache = DataSquare() def #()s, x, Sí.): si s.cache.#()x, Sí.) == Ninguno: s.cache.set()x, Sí., s.decorada.#()x, Sí.) retorno s.cache.#()x, Sí.)clase MaxDecorator: def __init_()s, decorada, max): s.decorada = decorada s.max = max def #()s, x, Sí.): si s.decorada.#()x, Sí.)  s.max: retorno s.max retorno s.decorada.#()x, Sí.)clase MinDecorator: def __init_()s, decorada, min): s.decorada = decorada s.min = min def #()s, x, Sí.): si s.decorada.#()x, Sí.) . s.min: retorno s.min retorno s.decorada.#()x, Sí.)clase VisibilidadDecorador: def __init_()s, decorada): s.decorada = decorada def #()s, x, Sí.): retorno s.decorada.#()x, Sí.) def dibujo()s): para Sí. dentro rango()10): para x dentro rango()10): impresión "%3d" % s.#()x, Sí.), impresión# Ahora, construir un oleoducto de decoradores:random_square = RandomSquare()635)random_cache = CacheDecorator()random_square)max_filtered = MaxDecorator()random_cache, 200)min_filtered = MinDecorator()max_filtered, 100)final = VisibilidadDecorador()min_filtered)final.dibujo()

Nota:

No confunda el patrón Decorator (o una implementación de este patrón de diseño en Python, como en el ejemplo anterior) con Python Decorators, una característica del lenguaje Python. Son cosas diferentes.

Segundo a la wiki de Python:

El patrón de decoración es un patrón descrito en el libro Patrones de diseño. Es una forma de aparentemente modificar el comportamiento de un objeto, cerrándolo dentro de un objeto de decoración con una interfaz similar. Esto no debe confundirse con los decoradores de Python, que es una característica de lenguaje para modificar dinámicamente una función o clase.

Cristal

abstracto clase Café abstracto def Costo abstracto def ingredientesfinal# Ampliación de un café simpleclase SimpleCoffee . Café def Costo 1.0 final def ingredientes "Coffee" finalfinal# Decorador abstractoclase CoffeeDecorator . Café protegida Getter decorado_coffee : Café def inicialización()@decorated_coffee) final def Costo decorado_coffee.Costo final def ingredientes decorado_coffee.ingredientes finalfinalclase ConMilk . CoffeeDecorator def Costo super + 0.5 final def ingredientes super + ", Milk" finalfinalclase ConSprinkles . CoffeeDecorator def Costo super + 0.2 final def ingredientes super + ", espolvoritos" finalfinalclase Programa def impresión()café : Café) puts "Cost: #{café.Costo}; Ingredientes: #{café.ingredientes}" final def inicialización café = SimpleCoffee.nuevo impresión()café) café = ConMilk.nuevo()café) impresión()café) café = ConSprinkles.nuevo()café) impresión()café) finalfinalPrograma.nuevo

Salida:

Costo: 1.0; Ingredientes: Café
Costo: 1,5; Ingredientes: Café, Leche
Costo: 1.7; Ingredientes: Café, Leche, Espolvoreas

C#

namespace WikiDesignPatterns;público interfaz IBike{} cuerda ObtenerDetalles(); doble GetPrice();}público clase AluminioBike : IBike{} público doble GetPrice() = 100.0; público cuerda ObtenerDetalles() = "Bicicleta de aluminio";}público clase CarbonBike : IBike{} público doble GetPrice() = 1000.0; público cuerda ObtenerDetalles() = "Carbon";}público abstracto clase BicicletaAccesorios : IBike{} privado solo IBike _bike; público BicicletaAccesorios()IBike bicicleta) {} _bike = bicicleta; } público virtual doble GetPrice() = _bike.GetPrice(); público virtual cuerda ObtenerDetalles() = _bike.ObtenerDetalles();}público clase Seguridad Paquete : BicicletaAccesorios{} público Seguridad Paquete()IBike bicicleta):base()bicicleta) {} } público Anulación cuerda ObtenerDetalles() = base.ObtenerDetalles() + " + paquete de seguridad"; público Anulación doble GetPrice() = base.GetPrice() + 1;}público clase SportPackage : BicicletaAccesorios{} público SportPackage()IBike bicicleta) : base()bicicleta) {} } público Anulación cuerda ObtenerDetalles() = base.ObtenerDetalles() + " + Paquete Deportivo"; público Anulación doble GetPrice() = base.GetPrice() + 10;}público clase BikeShop{} público estática vacío UpgradeBike() {} Var básico = nuevo AluminioBike(); BicicletaAccesorios Actualización = nuevo SportPackage()básico); Actualización = nuevo Seguridad Paquete()Actualización); Consola.WriteLine()$"Bike: '{upgraded.GetDetails()}' Costo: {upgraded.GetPrice()}"); }}

Salida:

Bicicleta: 'Bicicleta de aluminio + paquete deportivo + paquete de seguridad' Costo: 111


Rubí

clase AbstractCoffee def impresión puts "Cost: #{Costo}; Ingredientes: #{ingredientes}" finalfinalclase SimpleCoffee . AbstractCoffee def Costo 1.0 final def ingredientes "Coffee" finalfinalclase ConMilk . SimpleDelegator def Costo __getobj__.Costo + 0.5 final def ingredientes __getobj__.ingredientes + ", Milk" finalfinalclase ConSprinkles . SimpleDelegator def Costo __getobj__.Costo + 0.2 final def ingredientes __getobj__.ingredientes + ", espolvoritos" finalfinalcafé = SimpleCoffee.nuevocafé.impresióncafé = ConMilk.nuevo()café)café.impresióncafé = ConSprinkles.nuevo()café)café.impresión


Producción:

Costo: 1.0; Ingredientes: Café
Costo: 1,5; Ingredientes: Café, Leche
Costo: 1.7; Ingredientes: Café, Leche, Espolvoreas