En la programación orientada a objetos , el patrón de decorador es un patrón de diseño que permite agregar comportamiento a un objeto individual , dinámicamente, sin afectar el comportamiento de otros objetos de la misma clase . [1] El patrón de decorador suele ser útil para adherirse al principio de responsabilidad única , ya que permite dividir la funcionalidad entre clases con áreas de interés únicas. [2] El uso del decorador puede ser más eficiente que la subclasificación, porque el comportamiento de un objeto se puede aumentar sin definir un objeto completamente nuevo.
Descripción general
El patrón de diseño del decorador [3] es uno de los veintitrés patrones de diseño de GoF más 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 solucionar?
- Las responsabilidades deben agregarse (y eliminarse) de un objeto dinámicamente en tiempo de ejecución. [4]
- Debe proporcionarse una alternativa flexible a la subclasificación para ampliar la funcionalidad.
Al usar subclases, diferentes subclases extienden 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. [ cita requerida ]
¿Qué solución describe?
Definir Decorator
objetos que
- Implementar la interfaz del objeto extendido (decorado) (
Component
) de forma transparente reenviando todas las solicitudes. - realizar funciones adicionales antes / después de reenviar una solicitud.
Esto permite trabajar con diferentes Decorator
objetos para ampliar la funcionalidad de un objeto de forma dinámica en tiempo de ejecución.
Consulte también el diagrama de secuencia y clase UML a continuación.
Intención
El patrón de decorador se puede utilizar 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 tiempo de diseño. Esto se logra diseñando una nueva clase de Decorador que envuelve la clase original. Esta envoltura podría lograrse mediante la siguiente secuencia de pasos:
- Subclasifique la clase Component original en una clase Decorator (vea el diagrama UML);
- En la clase Decorator , agregue un puntero de componente como campo;
- En la clase Decorator , pase un Componente al constructor Decorator para inicializar el puntero del Componente ;
- En el decorador de clase, reenviar todos los componentes métodos al componente puntero; y
- En la clase ConcreteDecorator, anule cualquier método de componente cuyo comportamiento deba modificarse.
Este patrón está diseñado para que se puedan apilar varios decoradores uno encima del otro, agregando cada vez 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 las versiones decoradas como sin decorar.
Las características de decoración (por ejemplo, métodos, propiedades u otros miembros) generalmente se definen por una interfaz, mixin (también conocido como rasgo ) o herencia de clase que es compartida por 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 de decorador es una alternativa a la subclasificación . La subclasificación 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 ampliar 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 pueden combinarse por uso. Las implementaciones de I / O Streams de Java y .NET Framework incorporan el patrón decorador.
Motivación
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 de Ventana y asuma 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 uno también desea la capacidad de agregar bordes a las ventanas. Nuevamente, la clase Window original no tiene soporte. La subclase ScrollingWindow ahora plantea un problema, porque ha creado efectivamente un nuevo tipo de ventana. Si uno desea agregar soporte de borde a muchas ventanas, pero no a todas , debe crear subclases WindowWithBorder y ScrollingWindowWithBorder , etc. Este problema empeora con cada nueva característica o subtipo de ventana que se agrega. Para la solución de 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, se puede modificar la clase base. 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 método draw () y el método getDescription () que se requieren en este escenario, para decorar un control de ventana.
Casos de uso común
Aplicación de decoradores
Agregar o eliminar decoradores en el comando (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 Command . 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 seleccionados actualmente se envolverán en decoradores que modifican su función draw (), 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 eliminar 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 eliminar a granel. De manera similar, el patrón de diseño de estado se puede implementar usando 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 State sean más compositivos y capaces de manejar la complejidad arbitraria.
Uso en objetos de peso mosca
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 invariante que se comparte entre todos los objetos de peso mosca; y una variante, componente decorado que puede ser compartido parcialmente o no compartido por completo. Esta partición del objeto flyweight tiene como objetivo reducir el consumo de memoria. Los decoradores normalmente se almacenan en caché y también se reutilizan. Todos los decoradores contendrán una referencia común al objeto invariante compartido. Si el estado decorado es solo parcialmente variante, entonces los decoradores también se pueden compartir hasta cierto punto, aunque se debe tener cuidado de no alterar su estado mientras se utilizan. Consulte el artículo sobre el patrón de peso mosca para obtener más detalles. UITableView de iOS implementa el patrón de peso mosca 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 los decoradores
La aplicación de combinaciones de decoradores de diversas maneras a una colección de objetos presenta algunos problemas para interactuar con la colección de una manera que aproveche al máximo la funcionalidad agregada por los decoradores. El uso de un Adaptador o Visitante puede ser útil en tales casos. La interconexión 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 apoyan un enfoque compositivo más bien jerárquico y de arriba hacia abajo para ampliar la funcionalidad. Un decorador permite agregar o modificar el comportamiento de una interfaz en tiempo de ejecución. Se pueden utilizar 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 se puede escalar. Del mismo modo, intentar implementar la misma funcionalidad con propiedades aumenta cada instancia del objeto con propiedades innecesarias. Por las razones anteriores, los decoradores a menudo se consideran una alternativa eficiente en memoria a las subclases.
Los decoradores también se pueden utilizar para especializar objetos que no se pueden subclasificar, 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 de decorador también puede aumentar el patrón de fachada . Una fachada está diseñada para interactuar simplemente con el complejo sistema que encapsula, pero no agrega funcionalidad al sistema. Sin embargo, la envoltura de un sistema complejo proporciona un espacio que puede usarse para introducir nuevas funcionalidades basadas en la coordinación de subcomponentes en el sistema. Por ejemplo, un patrón de fachada puede unificar muchos diccionarios de idiomas diferentes en una interfaz de diccionario en varios idiomas. 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 como no limitados a envolver objetos individuales, sino que también son capaces de envolver grupos de objetos en este enfoque híbrido.
Alternativas a los decoradores
Como alternativa al patrón decorador, el adaptador se puede utilizar cuando la envoltura debe respetar una interfaz particular y debe soportar un comportamiento polimórfico , y la fachada cuando se desea una interfaz más fácil o más simple para un objeto subyacente. [5]
Patrón | Intención |
---|---|
Adaptador | Convierte una interfaz en otra para que coincida con lo que espera el cliente. |
Decorador | Agrega responsabilidad dinámicamente a la interfaz al envolver el código original |
Fachada | Proporciona una interfaz simplificada. |
Estructura
Diagrama de secuencia y clase UML
En el diagrama de clases de UML anterior , la Decorator
clase abstracta mantiene una referencia ( component
) al objeto decorado ( Component
) y le reenvía todas las solicitudes ( component.operation()
). Esto hace que sea Decorator
transparente (invisible) para los clientes de Component
.
Las subclases ( Decorator1
, Decorator2
) implementan un comportamiento adicional ( addBehavior()
) que se debe agregar al Component
(antes / después de enviarle una solicitud).
El diagrama de secuencia muestra las interacciones en tiempo de ejecución: el Client
objeto trabaja a través Decorator1
y los Decorator2
objetos para ampliar la funcionalidad de un Component1
objeto.
Las Client
llamadas operation()
en Decorator1
, que reenvía la solicitud a Decorator2
. Decorator2
realiza addBehavior()
después de reenviar la solicitud Component1
y vuelve a Decorator1
, que realiza addBehavior()
y vuelve a Client
.
Ejemplos de
Ir
paquete decologimportar ( "registro" "tiempo" )// OperateFn representa operaciones que requieren el tipo de decoración OperateFn func ()// Decora la operación func Decorate ( opFn OperateFn ) { diferir func ( s time . Time ) { log . Printf ( "tiempo transcurrido% 0.2d ms" , tiempo . Desde ( s ). Nanosegundos () / ( 1 << 20 )) } ( tiempo . Ahora ())// función de operación real opFn () }// paquete principal paquete principalimportar ( "github.com/tkstorm/go-design/structural/decorator/decolog" "log" "math / rand" "tiempo" )// salida: // 2019/08/19 19:05:24 finalizar acción a // 2019/08/19 19:05:24 tiempo transcurrido 77 ms // 2019/08/19 19:05:24 finalizar acción b // 2019/08/19 19:05:24 tiempo transcurrido 88 ms func main () { // decorate log a decolog . Decorate ( decolog . OperateFn ( DoActionA )) // decora log b decolog . Decorar ( decolog . OperateFn ( DoActionB )) }func DoActionA () { tiempo . Sueño ( tiempo . Duración ( rand . Intn ( 200 )) * tiempo . Milisegundos ) registro . Println ( "finalizar la acción a" ) }func DoActionB () { tiempo . Sueño ( tiempo . Duración ( rand . Intn ( 200 )) * tiempo . Milisegundos ) registro . Println ( "finalizar la acción b" ) }
C ++
Aquí se presentan dos opciones: primero, un decorador dinámico componible en tiempo de ejecución (tiene problemas para llamar a funciones decoradas a menos que se proxies explícitamente) y un decorador que usa herencia mixin.
Decorador dinámico
#include #include struct Shape { virtual ~ Shape () = predeterminado ; virtual std :: string GetName () const = 0 ; };struct Circle : Shape { void Resize ( factor flotante ) { radio * = factor ; } std :: string GetName () const override { return std :: string ( "Un círculo de radio" ) + std :: to_string ( radio ); } radio de flotación = 10.0f ; };struct ColoredShape : Shape { ColoredShape ( const std :: string & color , Shape * shape ) : color ( color ), shape ( forma ) {} std :: string GetName () const override { forma de retorno -> GetName () + "que es de color" + color ; } std :: color de la cadena ; Forma * forma ; }; int main () { Círculo círculo ; ColoredShape color_shape ( "rojo" , & círculo ); std :: cout << forma_coloreada . GetName () << std :: endl ;}
#include #include #include struct WebPage { virtual void display () = 0 ; virtual ~ WebPage () = predeterminado ; };struct BasicWebPage : Página Web { std :: string html ; void display () override { std :: cout << "Página WEB básica" << std :: endl ; } ~ BasicWebPage () = predeterminado ; };struct WebPageDecorator : Página Web { WebPageDecorator ( std :: unique_ptr < Página Web > página web ) : _webPage ( std :: movimiento ( página web )) { } void display () de anulación { _webPage -> pantalla (); } ~ WebPageDecorator () = predeterminado ; privada : std :: unique_ptr < Página Web > _webPage ; };struct AuthenticatedWebPage : WebPageDecorator { AuthenticatedWebPage ( std :: unique_ptr < Página Web > página web ) : WebPageDecorator ( std :: movimiento ( página web )) {} void authenticateUser () { std :: cout << "autenticación realizada" << std :: endl ; } Void display () override { authenticateUser (); WebPageDecorator :: display (); } ~ AuthenticatedWebPage () = predeterminado ; };struct AuthorizedWebPage : WebPageDecorator { AuthorizedWebPage ( std :: unique_ptr < Página Web > página web ) : WebPageDecorator ( std :: movimiento ( página web )) {} void AuthorizedUser () { std :: cout << "autorizado hecho" << std :: endl ; } anular visualización () { usuario autorizado (); WebPageDecorator :: display (); } ~ AuthorizedWebPage () = predeterminado ; }; int principal ( int argc , char * argv []) { std :: unique_ptr < Página Web > miPágina = std :: make_unique < BasicWebPage > (); myPage = std :: make_unique < AuthorizedWebPage > ( std :: move ( myPage )); myPage = std :: make_unique < AuthenticatedWebPage > ( std :: move ( myPage )); myPage -> display (); std :: cout << std :: endl ; return 0 ; }
Decorador estático (herencia de Mixin)
Este ejemplo demuestra una implementación de Decorator estática, que es posible debido a la capacidad de C ++ de heredar del argumento de la plantilla.
#include #include struct Circle { void Resize ( factor flotante ) { radio * = factor ; } std :: string GetName () const { return std :: string ( "Un círculo de radio" ) + std :: to_string ( radio ); } radio de flotación = 10.0f ; };plantilla < typename T > struct ColoredShape : public T { ColoredShape ( const std :: string & color ) : color ( color ) {} std :: string GetName () const { return T :: GetName () + "que es de color" + color ; } std :: color de la cadena ; };int main () { ColoredShape < Circle > red_circle ( "rojo" ); std :: cout << círculo_rojo . GetName () << std :: endl ; círculo_rojo . Cambiar tamaño ( 1.5f ); std :: cout << círculo_rojo . 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 interfaz pública de la clase de interfaz Window Window { void draw (); // Dibuja la cadena de ventana getDescription (); // Devuelve una descripción de la ventana }// Implementación de una ventana simple sin ninguna clase de barras de desplazamiento SimpleWindow implementa Window { @Override public void draw () { // Dibujar ventana } @Override public String getDescription () { return "simple window" ; } }
Las siguientes clases contienen los decoradores para todas las clases de Ventana , incluidas las propias clases de decoradores.
// clase de decorador abstracto - tenga en cuenta que implementa la clase abstracta de ventana WindowDecorator implementa Window { ventana final privada windowToBeDecorated ; // la ventana se está decorando public WindowDecorator ( Window windowToBeDecorated ) { esto . windowToBeDecorated = windowToBeDecorated ; } @Override public void draw () { windowToBeDecorated . dibujar (); // Delegación } @Override public String getDescription () { return windowToBeDecorated . getDescription (); // Delegación } }// El primer decorador de hormigón que agrega la clase de funcionalidad de barra de desplazamiento vertical VerticalScrollBarDecorator extiende WindowDecorator { public VerticalScrollBarDecorator ( Window windowToBeDecorated ) { super ( windowToBeDecorated ); } @Override public void draw () { super . dibujar (); drawVerticalScrollBar (); } private void drawVerticalScrollBar () { // Dibuja la barra de desplazamiento vertical } @Override public String getDescription () { return super . getDescription () + ", incluidas las barras de desplazamiento verticales" ; } }// El segundo decorador de hormigón que agrega la clase de funcionalidad de barra de desplazamiento horizontal HorizontalScrollBarDecorator extiende WindowDecorator { public HorizontalScrollBarDecorator ( Window windowToBeDecorated ) { super ( windowToBeDecorated ); } @Override public void draw () { super . dibujar (); drawHorizontalScrollBar (); } private void drawHorizontalScrollBar () { // Dibuja la barra de desplazamiento horizontal } @Override public String getDescription () { return super . getDescription () + ", incluidas las barras de desplazamiento horizontales" ; } }
Aquí hay un programa de prueba que crea una instancia de Windows que está completamente decorada (es decir, con barras de desplazamiento verticales y horizontales) e imprime su descripción:
public class DecoratedWindowTest { public static void main ( String [] args ) { // Crea una ventana decorada con barras de desplazamiento horizontales y verticales Window decorWindow = new HorizontalScrollBarDecorator ( new VerticalScrollBarDecorator ( new SimpleWindow ())); // Imprime la descripción de la ventana System . fuera . println ( ventana decorada . getDescription ()); } }
El resultado de este programa es una "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
importar org.junit.Assert.assertEquals estático ;import org.junit.Test ; clase pública WindowDecoratorTest { @Test public void testWindowDecoratorTest () { Ventana decoradaWindow = new HorizontalScrollBarDecorator ( nuevo VerticalScrollBarDecorator ( nuevo SimpleWindow ())); // afirman que la descripción de hecho incluye horizontales + vertical de barras de desplazamiento assertEquals ( "ventana simple, incluyendo barras de desplazamiento verticales, incluyendo barras de desplazamiento horizontal" , decoratedWindow . GetDescription ()) } }
Segundo ejemplo (escenario de preparación de café)
El siguiente ejemplo de Java ilustra el uso de decoradores con un escenario de preparación de café. En este ejemplo, el escenario solo incluye el costo y los ingredientes.
// La interfaz Coffee define la funcionalidad de Coffee implementada por la interfaz pública del decorador Coffee { public double getCost (); // Devuelve el costo del café público String getIngredients (); // Devuelve los ingredientes del café }// Extensión de un café simple sin ningún ingrediente adicional public class SimpleCoffee implementa Coffee { @Override public double getCost () { return 1 ; } @Override public String getIngredients () { return "Coffee" ; } }
Las siguientes clases contienen los decoradores para todas las clases de café , incluidas las clases de decoradores en sí.
// Clase de decorador abstracto - tenga en cuenta que implementa la interfaz Coffee. La clase abstracta pública CoffeeDecorator implementa Coffee { café final privado café decorado ; public CoffeeDecorator ( Coffee c ) { this . decoradoCoffee = c ; } @Override public double getCost () { // Implementando los métodos de la interfaz, devuelve decorCoffee . getCost (); } @Override public String getIngredients () { return decoradoCoffee . getIngredients (); } }// Decorator WithMilk mezcla la leche con el café. // Tenga en cuenta que extiende CoffeeDecorator. la clase WithMilk extiende CoffeeDecorator { public WithMilk ( Coffee c ) { super ( c ); } @Override public double getCost () { // Los métodos de reemplazo definidos en la superclase abstracta return super . getCost () + 0.5 ; } @Override public String getIngredients () { return super . getIngredients () + ", Leche" ; } }// Decorator WithSprinkles mezcla las gotas sobre el café. // Tenga en cuenta que extiende CoffeeDecorator. la clase WithSprinkles extiende CoffeeDecorator { público WithSprinkles ( Coffee c ) { super ( c ); } @Override public double getCost () { return super . getCost () + 0.2 ; } @Override public String getIngredients () { return super . getIngredients () + ", Sprinkles" ; } }
Aquí hay un programa de prueba que crea un Instancia de café que está completamente decorada (con leche y chispas), calcula el costo del café e imprime sus ingredientes:
public class Main { public static void printInfo ( Coffee c ) { System . fuera . println ( "Costo:" + c . getCost () + "; Ingredientes:" + c . getIngredients ()); } public static void main ( String [] args ) { Coffee c = new SimpleCoffee (); printInfo ( c ); c = nuevo WithMilk ( c ); printInfo ( c ); c = nuevo WithSprinkles ( c ); printInfo ( c ); } }
El resultado de este programa se da a continuación:
Costo: 1.0; Ingredientes: CaféCoste: 1,5; Ingredientes: Café, LecheCoste: 1,7; Ingredientes: Café, Leche, Sprinkles
PHP
Componente de clase abstracta { datos $ protegidos ; valor $ protegido ; función pública abstracta getData (); función pública abstracta getValue (); } class ConcreteComponent extiende Component { función pública __construct () { $ esto -> valor = 1000 ; $ esto -> datos = "Componente de hormigón: \ t { $ esto -> valor } \ n " ; } función pública getData () { return $ this -> data ; } función pública getValue () { devuelve $ esto -> valor ; } }El decorador de clase abstracta extiende el componente { }class ConcreteDecorator1 extiende Decorator { función pública __construct ( Componente $ datos ) { $ esto -> valor = 500 ; $ esto -> datos = $ datos ; } función pública getData () { return $ this -> data -> getData () . "Decorador de hormigón 1: \ t { $ esto -> valor } \ n " ; } función pública getValue () { devolver $ esto -> valor + $ esto -> datos -> getValue (); } }class ConcreteDecorator2 extiende Decorator { función pública __construct ( Componente $ datos ) { $ esto -> valor = 500 ; $ esto -> datos = $ datos ; } función pública getData () { return $ this -> data -> getData () . "Decorador de hormigón 2: \ t { $ esto -> valor } \ n " ; } función pública getValue () { devolver $ esto -> valor + $ esto -> datos -> getValue (); } }clase Cliente { componente $ privado ; función pública __construct () { $ this -> componente = new ConcreteComponent (); $ esto -> componente = $ esto -> wrapComponent ( $ esto -> componente ); echo $ this -> componente -> getData (); echo "Cliente: \ t \ t \ t " ; echo $ this -> componente -> getValue (); } función privada wrapComponent ( Componente $ componente ) { $ componente1 = new ConcreteDecorator1 ( $ componente ); $ componente2 = nuevo ConcreteDecorator2 ( $ componente1 ); return $ componente2 ; } }$ cliente = nuevo Cliente ();// Resultado: # quanton81// Componente de hormigón: 1000 // Decorador de hormigón 1: 500 // Decorador de hormigón 2: 500 // Cliente: 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 una cuadrícula de 10x10 de valores 0-255. " ""importar al azardef s32_to_u16 ( x ): si x < 0 : sign = 0xF000 else : sign = 0 bottom = x & 0x00007FFF return bottom | firmardef seed_from_xy ( x , y ): return s32_to_u16 ( x ) | ( s32_to_u16 ( y ) << 16 )clase RandomSquare : def __init__ ( s , seed_modifier ): s . seed_modifier = seed_modifier def get ( s , x , y ): seed = seed_from_xy ( x , y ) ^ s . seed_modifier aleatorio . semilla ( semilla ) regresa aleatoriamente . randint ( 0 , 255 )clase DataSquare : def __init__ ( s , initial_value = None ): s . datos = [ valor_inicial ] * 10 * 10 def get ( s , x , y ): devuelve s . datos [( y * 10 ) + x ] # sí: estos son todos 10x10 def conjunto ( s , x , y , u ): s . datos [( y * 10 ) + x ] = uclase CacheDecorator : def __init__ ( s , decorado ): s . decorado = decorado s . caché = DataSquare () def get ( s , x , y ): si s . caché . get ( x , y ) == Ninguno : s . caché . set ( x , y , s . decorado . get ( x , y )) return s . caché . obtener ( x , y )clase MaxDecorator : def __init__ ( s , decorado , max ): s . decorado = decorado s . max = max def get ( s , x , y ): si s . decorado . obtener ( x , y ) > s . max : retorno s . retorno máximo s . decorado . obtener ( x , y ) clase MinDecorator : def __init__ ( s , decorado , min ): s . decorado = decorado s . min = min def get ( s , x , y ): si s . decorado . obtener ( x , y ) < s . min : devuelve s . min retorno s . decorado . obtener ( x , y )clase VisibilityDecorator : def __init__ ( s , decorado ): s . decorado = decorado def get ( s , x , y ): devuelve s . decorado . obtener ( x , y ) def dibujar ( s ): para y en el rango ( 10 ): para x en el rango ( 10 ): imprimir " % 3d " % s . obtener ( x , y ), imprimir# Ahora, cree una línea de decoradores:random_square = RandomSquare ( 635 ) random_cache = CacheDecorator ( random_square ) max_filtered = MaxDecorator ( random_cache , 200 ) min_filtered = MinDecorator ( max_filtered , 100 ) final = VisibilityDecorator ( min_filtered )final . dibujar ()
Nota:
No confunda el patrón Decorator (o una implementación de este patrón de diseño en Python, como 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 decorador es un patrón descrito en el libro de patrones de diseño. Es una forma de modificar aparentemente el comportamiento de un objeto, encerrándolo dentro de un objeto de decoración con una interfaz similar. Esto no debe confundirse con Python Decorators, que es una característica del lenguaje para modificar dinámicamente una función o clase. [7]
Cristal
abstracta clase de café abstracta definición de costes abstracta def ingredientes finales# Extensión de una clase de café simple SimpleCoffee < Coffee def cost 1 . 0 final def ingredientes "Coffee" end endResumen # decorador clase CoffeeDecorator < café protegida captador decorated_coffee : Café def initialize ( @decorated_coffee ) fin def costó café_decorado . fin de costo def ingredientes café_decorado . ingredientes fin finclass WithMilk < CoffeeDecorator def cost super + 0 . 5 fin def ingredientes super + ", Milk" end endclass WithSprinkles < CoffeeDecorator def cost super + 0 . 2 fin def ingredientes super + ", Sprinkles" end endclase Programa def imprimir ( café : café ) pone "Costo: {# café . coste } ; Ingredientes: # { café . ingredientes } " final def initialize coffee = SimpleCoffee . nueva impresión ( café ) café = WithMilk . nuevo ( café ) impresión ( café ) café = WithSprinkles . nuevo ( café ) imprimir ( café ) fin finPrograma . nuevo
Producción:
Costo: 1.0; Ingredientes: CaféCoste: 1,5; Ingredientes: Café, LecheCoste: 1,7; Ingredientes: Café, Leche, Sprinkles
C#
espacio de nombres WikiDesignPatterns { interfaz pública IBike { string GetDetails (); doble GetPrice (); } AluminiumBike de clase pública : IBike { public double GetPrice () { return 100 ; } public string GetDetails () { return "Bicicleta de aluminio" ; } } público de clase CarbonBike : Ibike { públicas doble GetPrice () { retorno 1000 ; } public string GetDetails () { return "Carbon" ; } } BikeAccessories de clase abstracta pública : IBike { IBike _bike privada de solo lectura ; BikeAccessories públicos ( bicicleta IBike ) { _bike = bicicleta ; } público virtual doble GetPrice () { return _bike . GetPrice (); } cadena virtual pública GetDetails () { return _bike . GetDetails (); } } SecurityPackage de clase pública : BikeAccessories { Paquete de seguridad público ( bicicleta IBike ): base ( bicicleta ) { } cadena de anulación pública GetDetails () { base de retorno . GetDetails () + "+ Paquete de seguridad" ; } public override double GetPrice () { base de retorno . GetPrice () + 1 ; } } público de clase SportPackage : BikeAccessories { público SportPackage ( Ibike moto ) : la base ( moto ) { } cadena de anulación pública GetDetails () { base de retorno . GetDetails () + "+ Paquete deportivo" ; } public override double GetPrice () { base de retorno . GetPrice () + 10 ; } } público de clase Bikeshop { pública estática vacío UpgradeBike () { var basicBike = nueva AluminiumBike (); BikeAccessories actualizado = nuevo SportPackage ( basicBike ); actualizado = nuevo paquete de seguridad ( actualizado ); Consola . WriteLine ( $ "Bicicleta: '{actualizado.GetDetails ()}' Costo: {actualizado.GetPrice ()}" ); } } }
Producción:
Bicicleta: 'Bicicleta de aluminio + Paquete deportivo + Paquete de seguridad' Costo: 111
Ver también
- Patrón compuesto
- Patrón de adaptador
- Clase abstracta
- Fábrica abstracta
- Programación Orientada a Aspectos
- Objeto inmutable
Referencias
- ^ Gamma, Erich; et al. (1995). Patrones de diseño . Reading, MA: Addison-Wesley Publishing Co, Inc. págs. 175 y siguientes . ISBN 0-201-63361-2.
- ^ "Cómo implementar un patrón de decorador" . Archivado desde el original el 7 de julio de 2015.
- ^ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Patrones de diseño: elementos de software orientado a objetos reutilizable . Addison Wesley. págs. 175ff . ISBN 0-201-63361-2.CS1 maint: varios nombres: lista de autores ( enlace )
- ^ "El patrón de diseño del decorador: problema, solución y aplicabilidad" . w3sDesign.com . Consultado el 12 de agosto de 2017 .
- ^ Freeman, Eric; Freeman, Elisabeth; Sierra, Kathy; Bates, Bert (2004). Hendrickson, Mike; Loukides, Mike (eds.). Head First Design Patterns (rústica) . 1 . O'Reilly. págs. 243, 252, 258, 260. ISBN 978-0-596-00712-6. Consultado el 2 de julio de 2012 .
- ^ "El patrón de diseño del decorador - Estructura y colaboración" . w3sDesign.com . Consultado el 12 de agosto de 2017 .
- ^ "DecoratorPattern - Python Wiki" . wiki.python.org .
enlaces externos
- Implementación de Decorator Pattern en Java
- Descripción del patrón de decorador del repositorio de patrones de Portland