En 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 otras instancias de la misma clase . [1] El patrón 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], así como al principio abierto-cerrado , al permitir la funcionalidad de un clase que se ampliará sin ser modificada. [3] El uso de decoradores 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.
El patrón de diseño decorador [4] es uno de los veintitrés patrones de diseño 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.
Cuando se utilizan 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. [ cita necesaria ]
Definir Decorator
objetos que
Component
) de forma transparente reenviándole todas las solicitudesEsto permite trabajar con diferentes Decorator
objetos para ampliar la funcionalidad de un objeto dinámicamente en tiempo de ejecución.
Consulte también el diagrama de secuencia y clase UML a continuación.
El patrón 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 realice algún trabajo preliminar en tiempo de diseño. Esto se logra diseñando una nueva clase Decorador que envuelve la clase original. Esta envoltura podría lograrse mediante la siguiente secuencia de pasos:
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 operación() estaba disponible tanto en la versión decorada como sin decorar.
Las características de decoración (por ejemplo, métodos, propiedades u otros miembros) generalmente se definen mediante una interfaz, mixin (también conocido como rasgo ) o herencia de clases 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 la subclasificación . Las subclases añaden 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 objetos seleccionados.
Esta diferencia adquiere mayor importancia cuando existen 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 se tendría que crear 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 su uso. Las implementaciones de I/O Streams tanto de Java como de .NET Framework incorporan el patrón decorador.
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, supongamos que uno también desea poder agregar bordes a las ventanas. Nuevamente, la clase Window original no tiene soporte. La subclase ScrollingWindow ahora plantea un problema, porque efectivamente ha creado un nuevo tipo de ventana. Si se desea agregar soporte de bordes a muchas ventanas, pero no a todas , se deben 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 decoradora, se crea un nuevo BorderedWindowDecorator . Cualquier combinación de ScrollingWindowDecorator o BorderedWindowDecorator puede decorar ventanas existentes. Si es necesario agregar la funcionalidad a todos los Windows, se puede modificar la clase base. Por otro lado, a veces (por ejemplo, usando frameworks 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.
Agregar o eliminar decoradores mediante una orden (como presionar un botón) es un patrón de interfaz de usuario común, que a menudo se implementa junto con el patrón de diseño Comando . Por ejemplo, una aplicación de edición de texto podría tener un botón para resaltar texto. Al presionar el botón, los glifos de texto individuales actualmente seleccionados estarán todos envueltos 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 cambios de estado es otro caso de uso común. Dependiendo del alcance del estado, los decoradores se pueden aplicar o eliminar en masa. De manera similar, el patrón de diseño State 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 State sean más compositivos y capaces de manejar una complejidad arbitraria.
La decoración también se utiliza a menudo en el patrón de diseño Flyweight . Los objetos Flyweight se dividen en dos componentes: un componente invariante que se comparte entre todos los objetos Flyweight; y una variante, componente decorado que puede ser parcialmente compartido o completamente no compartido. 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 invariante compartido. Si el estado decorado es sólo 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. 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é/se reutilizan.
Aplicar combinaciones de decoradores de diversas maneras a una colección de objetos introduce 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 resultar útil en tales casos. La interacción con múltiples capas de decoradores plantea desafíos adicionales y la lógica de los Adaptadores y Visitantes debe diseñarse para tenerlo en cuenta.
Los decoradores apoyan un enfoque compositivo más que jerárquico de arriba hacia abajo para ampliar la funcionalidad. Un decorador permite agregar o alterar el comportamiento de una interfaz en tiempo de ejecución. Se pueden utilizar para envolver objetos en una combinación arbitraria de múltiples capas. Hacer lo mismo con las subclases significa implementar redes complejas de herencia múltiple, que son ineficientes en memoria y, en cierto punto, simplemente no pueden escalarse. Del mismo modo, intentar implementar la misma funcionalidad con propiedades sobrecarga cada instancia del objeto con propiedades innecesarias. Por las razones anteriores, los decoradores a menudo se consideran una alternativa a las subclases que ahorra memoria.
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 menciona en otra parte) o, en general, objetos que carecen de alguna funcionalidad necesaria.
El patrón decorador también puede aumentar el patrón 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 del sistema. Por ejemplo, un patrón de fachada puede unificar muchos diccionarios de idiomas diferentes 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 como algo que no se limita a envolver objetos individuales, sino que también es capaz de envolver grupos de objetos en este enfoque híbrido.
Como alternativa al patrón decorador, el adaptador se puede utilizar cuando el envoltorio debe respetar una interfaz particular y debe admitir un comportamiento polimórfico , y Facade cuando se desea una interfaz más fácil o más simple para un objeto subyacente. [6]
En el diagrama de clases UML anterior , la clase abstracta mantiene una referencia ( ) al objeto decorado ( ) y le reenvía todas las solicitudes ( ). Esto lo hace transparente (invisible) para los clientes de .Decorator
component
Component
component.operation()
Decorator
Component
Las subclases ( Decorator1
, Decorator2
) implementan un comportamiento adicional ( addBehavior()
) que debe agregarse Component
(antes/después de reenviarle una solicitud).
El diagrama de secuencia muestra las interacciones en tiempo de ejecución: el Client
objeto funciona a través de objetos Decorator1
y Decorator2
para ampliar la funcionalidad de un Component1
objeto.
Las Client
llamadas operation()
a Decorator1
, que reenvían la solicitud a Decorator2
. Decorator2
realiza addBehavior()
después de reenviar la solicitud Component1
y regresa a Decorator1
, que realiza addBehavior()
y regresa a Client
.
Esta implementación se basa en la implementación anterior a C++ 98 en el libro.
#incluir <iostream> #incluir <memoria> // Interfaz de bebidas. clase Bebida { público : bebida virtual vacía () = 0 ; virtual ~ Bebida () = predeterminado ; }; // Bebidas que se pueden decorar. clase Café : bebida pública { pública : bebida virtual nula () override { std :: cout << "Beber café" ; } }; class Soda : bebida pública { public : bebida virtual nula () override { std :: cout << "Beber refresco" ; } }; clase BeverageDecorator : bebida pública { public : BeverageDecorator () = eliminar ; BeverageDecorator ( std :: Unique_ptr <Bebida> componente_ ) : componente ( std :: mover ( componente_ ) ) {} bebida virtual vacía ( ) = 0 ; protegido : void callComponentDrink () { if ( componente ) { componente -> beber (); } } privado : std :: unique_ptr <Bebida> componente ; }; clase Leche : public BeverageDecorator { public : Leche ( std :: Unique_ptr <Bebida> componente_ , porcentaje flotante_ ) : BebidaDecorator ( std :: mover ( componente_ ) ) , porcentaje ( porcentaje_ ) { } bebida virtual nula () anular { callComponentDrink (); std :: cout << ", con leche de riqueza " << porcentaje << "%" ; } privado : porcentaje de flotación ; }; class IceCubes : public BeverageDecorator { public : IceCubes ( std :: Unique_ptr <Bebida> componente_ , int count_ ) : BeverageDecorator ( std :: move ( componente_ ) ) , count ( count_ ) { } bebida virtual nula () anular { callComponentDrink (); std :: cout << ", con " << contar << " cubitos de hielo " ; } privado : recuento int ; }; class Azúcar : public BeverageDecorator { public : Sugar ( std :: Unique_ptr < Bebida > componente_ , int cucharas_ ) : BebidaDecorator ( std :: mover ( componente_ )) , cucharas ( cucharas_ ) { } bebida virtual nula () anular { callComponentDrink (); std :: cout << ", con " << cucharadas << " cucharadas de azúcar" ; } privado : int cucharas = 1 ; }; int main () { std :: Unique_ptr < Bebida > refresco = std :: make_unique < Refresco > (); refresco = std :: make_unique < IceCubes > ( std :: mover ( refresco ), 3 ); refresco = std :: make_unique < Azúcar > ( std :: mover ( refresco ), 1 ); refresco -> beber (); std :: cout << std :: endl ; std :: Unique_ptr < Bebida > café = std :: make_unique < Café > (); café = std :: make_unique < IceCubes > ( std :: mover ( café ), 16 ); café = std :: make_unique < Leche > ( std :: mover ( café ), 3. ); café = std :: make_unique < Azúcar > ( std :: mover ( café ), 2 ); café -> beber (); devolver 0 ; }
La salida del programa es como
Beber Gaseosa , con 3 cubitos de hielo , con 1 cucharada de azúcar Beber Café , con 16 cubitos de hielo , con leche de riqueza 3 % , con 2 cucharadas de azúcar
El ejemplo completo se puede probar en una página de Godbolt.
Aquí se presentan dos opciones: primero, un decorador dinámico que se puede componer en tiempo de ejecución (tiene problemas al llamar a funciones decoradas a menos que se utilice proxy explícitamente) y un decorador que usa herencia mixin.
#incluir <iostream> #incluir <cadena> estructura Forma { virtual ~ Forma () = predeterminado ; virtual std :: cadena GetName () const = 0 ; }; estructura Círculo : Forma { void Cambiar tamaño ( 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 : Forma { ColoredShape ( const std :: cadena y color , Forma * forma ) : color ( color ), forma ( forma ) {} std :: string GetName () const override { return forma -> GetName () + "que está coloreado" + color ; } std :: color de cadena ; Forma * forma ; }; int main () { Círculo círculo ; ColoredShape coloured_shape ( "rojo" y círculo ) ; std :: cout << forma_coloreada . ObtenerNombre () << std :: endl ; }
#include <memoria> #include <iostream> #include <cadena> estructura página web { visualización vacía virtual () = 0 ; virtual ~ Página web () = predeterminado ; }; estructura BasicWebPage : página web { std :: cadena html ; void display () override { std :: cout << "Página WEB básica" << std :: endl ; } }; struct WebPageDecorator : Página web { WebPageDecorator ( std :: Unique_ptr < Página web > Página web ) : _Página web ( std :: mover ( Página web )) { } void display () anular { _webPage -> display (); } privado : std :: Unique_ptr < Página web > _Página web ; }; struct AuthenticatedWebPage : WebPageDecorator { AuthenticatedWebPage ( std :: Unique_ptr < WebPage > webPage ) : WebPageDecorator ( std :: move ( webPage )) {} void authenticateUser () { std :: cout << "autenticación realizada" << std :: endl ; } visualización nula () anular { autenticarUsuario (); WebPageDecorator :: mostrar (); } }; struct AuthorizedWebPage : WebPageDecorator { AuthorizedWebPage ( std :: Unique_ptr < WebPage > webPage ) : WebPageDecorator ( std :: move ( webPage )) {} void usuario autorizado () { std :: cout << "autorizado hecho" << std :: endl ; } void display () override { usuario autorizado (); WebPageDecorator :: mostrar (); } }; int main ( int argc , char * argv []) { std :: Unique_ptr < WebPage > miPágina = std :: make_unique < BasicWebPage > (); miPágina = std :: make_unique < Página Web Autorizada > ( std :: mover ( miPágina )); myPage = std :: make_unique < Página web autenticada > ( std :: move ( myPage )); miPágina -> mostrar (); std :: cout << std :: endl ; devolver 0 ; }
Este ejemplo demuestra una implementación de Decorador estático, que es posible gracias a la capacidad de C++ para heredar del argumento de la plantilla.
#incluir <iostream> #incluir <cadena> estructura Círculo { void Cambiar tamaño ( 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 :: cadena y color ) : color ( color ) {} std :: string GetName () const { return T :: GetName () + "que está coloreado" + color ; } std :: color de cadena ; }; int main () { ColoredShape < Círculo > red_circle ( "rojo" ); std :: cout << círculo_rojo . ObtenerNombre () << std :: endl ; circulo rojo . Cambiar tamaño ( 1.5f ); std :: cout << círculo_rojo . ObtenerNombre () << std :: endl ; }
El siguiente ejemplo de Java ilustra el uso de decoradores utilizando el escenario de ventana/desplazamiento.
// La clase de interfaz de ventana ventana de interfaz pública { void draw (); // Dibuja la cadena de ventana getDescription (); // Devuelve una descripción de la ventana } // Implementación de una ventana simple sin barras de desplazamiento class SimpleWindow implements Window { @Override public void draw () { // Dibujar ventana } @Override public String getDescription () { return "ventana simple" ; } }
Las siguientes clases contienen los decoradores de todas Window
las clases, incluidas las propias clases de decorador.
// clase de decorador abstracto: tenga en cuenta que implementa Window clase abstracta WindowDecorator implements Window { ventana final privada windowToBeDecorate ; // la ventana siendo decorada public WindowDecorator ( Ventana ventanaADecorar ) { this . ventanaToBeDecorada = ventanaToBeDecorada ; } @Override sorteo público vacío () { ventanaABeDecorada . dibujar (); //Delegación } @Override public String getDescription () { return windowToBeDecoated . obtenerDescripción (); //Delegación } } // El primer decorador concreto que agrega la funcionalidad de la barra de desplazamiento vertical class VerticalScrollBarDecorator extends WindowDecorator { public VerticalScrollBarDecorator ( Window windowToBeDecorate ) { super ( windowToBeDecorate ); } @Override sorteo público vacío () { super . dibujar (); dibujarVerticalScrollBar (); } 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 concreto que agrega la funcionalidad de la barra de desplazamiento horizontal class HorizontalScrollBarDecorator extends WindowDecorator { public HorizontalScrollBarDecorator ( Window windowToBeDecorate ) { super ( windowToBeDecorate ); } @Override sorteo público vacío () { super . dibujar (); dibujarHorizontalScrollBar (); } 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 Window
instancia 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 Ventana decoradaWindow = new HorizontalScrollBarDecorator ( new VerticalScrollBarDecorator ( new SimpleWindow ())); // Imprime la descripción de la ventana System . afuera . println ( ventanadecorada . getDescripción ()); } }
El resultado de este programa es "una ventana simple, que incluye barras de desplazamiento verticales y barras de desplazamiento horizontales". Observe cómo el getDescription
método de los dos decoradores primero recupera la Window
descripción del decorado y lo decora con un sufijo.
A continuación se muestra la clase de prueba JUnit para Test Driven Development.
importar org.junit.Assert.assertEquals estático ; importar org.junit.Test ; clase pública WindowDecoratorTest { @Test public void testWindowDecoratorTest () { Ventana decoradaWindow = new HorizontalScrollBarDecorator ( new VerticalScrollBarDecorator ( new SimpleWindow ())); // afirmar que la descripción efectivamente incluye barras de desplazamiento horizontales + verticales afirmarEquals ( "ventana simple, incluidas barras de desplazamiento verticales, incluidas barras de desplazamiento horizontales" , ventana decorada . getDescription ()); } }
El siguiente ejemplo de Java ilustra el uso de decoradores que utilizan un escenario de preparación de café. En este ejemplo, el escenario sólo incluye costos e ingredientes.
// La interfaz Coffee define la funcionalidad de Coffee implementada por el decorador public interface Coffee { public double getCost (); // Devuelve el coste del café public String getIngredients (); // Devuelve los ingredientes del café } // Extensión de un café simple sin ingredientes adicionales public class SimpleCoffee implements Coffee { @Override public double getCost () { return 1 ; } @Override public String getIngredients () { return "Café" ; } }
Las siguientes clases contienen los decoradores para todas las clases de Café , incluidas las propias clases de decoradores.
// Clase de decorador abstracto: tenga en cuenta que implementa la interfaz Coffee public abstract class CoffeeDecorator implements Coffee { private final Coffee decoradoCoffee ; public CoffeeDecorator ( Café c ) { this . decoradoCafé = c ; } @Override public double getCost () { // Implementación de métodos de la interfaz return decoradoCoffee . obtenerCosto (); } @Override public String getIngredients () { return café decorado . obtenerIngredientes (); } } // El decorador WithMilk mezcla leche con café. // Tenga en cuenta que extiende CoffeeDecorator. clase WithMilk extiende CoffeeDecorator { public WithMilk ( Coffee c ) { super ( c ); } @Override public double getCost () { // Anulando los métodos definidos en la superclase abstracta return super . getCosto () + 0,5 ; } @Override public String getIngredients () { return super . getIngredientes () + ", Leche" ; } } // Decorator WithSprinkles mezcla chispas sobre el café. // Tenga en cuenta que extiende CoffeeDecorator. clase WithSprinkles extiende CoffeeDecorator { público WithSprinkles ( Café c ) { super ( c ); } @Override public double getCost () { return super . getCosto () + 0,2 ; } @Override public String getIngredients () { return super . getIngredientes () + ", Sprinkles" ; } }
Aquí hay un programa de prueba que crea una instancia de Café que está completamente decorada (con leche y chispas), calcula el costo del café e imprime sus ingredientes:
clase pública Principal { public static void printInfo ( Café c ) { Sistema . afuera . println ( "Costo: " + c . getCosto () + "; Ingredientes: " + c . getIngredientes ()); } public static void main ( String [] args ) { Café c = new SimpleCoffee (); imprimirInfo ( c ); c = nuevo ConLeche ( c ); imprimirInfo ( c ); c = nuevo ConSprinkles ( c ); imprimirInfo ( c ); } }
El resultado de este programa se proporciona a continuación:
Costo: 1,0; Ingredientes: CaféCosto: 1,5; Ingredientes: Café, LecheCosto: 1,7; Ingredientes: Café, Leche, Sprinkles
Componente de clase abstracta { protegido $datos ; valor $ protegido ; función pública abstracta getData (); función pública abstracta getValue (); } clase ConcreteComponent extiende Componente { función pública __construct () { $this -> valor = 1000 ; $this -> data = "Componente de hormigón: \t { $this -> valor } \n " ; } función pública getData () { return $this -> datos ; } función pública getValue () { return $this -> valor ; } } El decorador de clase abstracta extiende el componente { }clase ConcreteDecorator1 extiende Decorador { función pública __construct ( Componente $datos ) { $this -> valor = 500 ; $esto -> datos = $datos ; } función pública getData () { return $this -> datos -> getData () . "Decorador de hormigón 1: \t { $this -> valor } \n " ; } función pública getValue () { return $this -> valor + $this -> datos -> getValue (); } }clase ConcreteDecorator2 extiende Decorador { función pública __construct ( Componente $datos ) { $this -> valor = 500 ; $esto -> datos = $datos ; } función pública getData () { return $this -> datos -> getData () . "Decorador de hormigón 2: \t { $this -> valor } \n " ; } función pública getValue () { return $this -> valor + $this -> datos -> getValue (); } }clase Cliente { privado $componente ; función pública __construct () { $this -> componente = nuevo ConcreteComponent (); $this -> componente = $this -> wrapComponent ( $this -> componente ); echo $this -> componente -> getData (); echo "Cliente: \t\t\t " ; echo $this -> componente -> getValue (); } función privada wrapComponent ( Componente $componente ) { $componente1 = nuevo ConcreteDecorator1 ( $componente ); $componente2 = nuevo ConcreteDecorator2 ( $componente1 ); devolver $componente2 ; } }$cliente = nuevo Cliente ();// Resultado: #quanton81//Componente de Concreto: 1000 //Decorador de Concreto 1: 500 //Decorador de Concreto 2: 500 //Cliente: 2000
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 con valores 0-255. """importar aleatoriamentedef s32_to_u16 ( x ): si x < 0 : signo = 0xF000 de lo contrario : signo = 0 abajo = x & 0x00007FFF volver abajo | firmardef semilla_de_xy ( x , y ): devuelve s32_to_u16 ( x ) | ( s32_to_u16 ( y ) << 16 )clase RandomSquare : def __init__ ( s , seed_modifier ): s . modificador_semilla = modificador_semilla def obtener ( s , x , y ): semilla = semilla_de_xy ( x , y ) ^ s . modificador_semilla aleatorio . semilla ( seed ) regresa aleatoriamente . randint ( 0 , 255 )clase DataSquare : def __init__ ( s , valor_inicial = Ninguno ): s . datos = [ valor_inicial ] * 10 * 10 def obtener ( s , x , y ): devolver s . datos [( y * 10 ) + x ] # sí: todos estos son 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 obtener ( 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 . máximo = máximo def obtener ( s , x , y ): si s . decorado . obtener ( x , y ) > s . máx : devolver s . retorno máximo s . decorado . obtener ( x , y ) clase MinDecorator : def __init__ ( s , decorado , min ): s . decorado = decorado s . min = min def obtener ( s , x , y ): si s . decorado . obtener ( x , y ) < s . min : retorno s . retorno mínimo s . decorado . obtener ( x , y ) clase VisibilityDecorator : def __init__ ( s , decorado ): s . decorado = decorado def obtener ( s , x , y ): devolver s . decorado . obtener ( x , y ) def dibujar ( s ): para y en el rango ( 10 ): para x en el rango ( 10 ): imprime " %3d " % s . obtener ( x , y ), imprimir# Ahora, crea una canalización 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 )definitiva . dibujar ()
Nota:
El patrón Decorator (o una implementación de este patrón de diseño en Python, como en el ejemplo anterior) no debe confundirse con los decoradores de Python , una característica del lenguaje Python. Son cosas diferentes.
En segundo lugar a la Wiki de Python:
El patrón 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 decorativo con una interfaz similar. Esto no debe confundirse con los decoradores de Python, que es una característica del lenguaje para modificar dinámicamente una función o clase. [8]
clase abstracta Café resumen def costo resumen def ingredientes final # Ampliación de una clase de café simple SimpleCoffee < Café definitivamente cuesta 1 . 0 fin def ingredientes "Café" fin fin # Clase de decorador abstracto CoffeeDecorator < Café protegido getter decorado_café : Café def inicializar ( @decorated_coffee ) finalizar def costo café_decorado . final del costo def ingredientes café_decorado . ingredientes fin fin clase WithMilk < CoffeeDecorator def costo super + 0 . 5 final def ingredientes super + ", Leche" fin fin clase WithSprinkles < CoffeeDecorator def costo super + 0 . 2 finales def ingredientes super + ", Sprinkles" fin fin clase Programa def print ( café : Café ) pone "Costo: #{ café . costo } ; Ingredientes: #{ café . ingredientes } " fin def inicializar café = SimpleCoffee . nueva impresión ( café ) cafe = Con Leche . nuevo ( café ) imprimir ( café ) café = Con chispas . nuevo ( café ) imprimir ( café ) fin fin Programa . nuevo
Producción:
Costo: 1,0; Ingredientes: CaféCosto: 1,5; Ingredientes: Café, LecheCosto: 1,7; Ingredientes: Café, Leche, Sprinkles
espacio de nombres WikiDesignPatterns ; interfaz pública IBike { cadena GetDetails (); doble ObtenerPrecio (); } bicicleta de aluminio de clase pública : IBike { public double GetPrice () => 100,0 ; cadena pública GetDetails () => "Bicicleta de aluminio" ; } clase pública CarbonBike : IBike { public double GetPrice () => 1000.0 ; cadena pública GetDetails () => "Carbono" ; } clase abstracta pública BikeAccessories : IBike { privada de solo lectura IBike _bike ; accesorios para bicicletas públicas ( bicicleta IBike ) { _bike = bicicleta ; } publico virtual doble GetPrice () => _bike . ObtenerPrecio (); cadena virtual pública GetDetails () => _bike . Obtén detalles (); } paquete de seguridad de clase pública : accesorios para bicicletas { paquete de seguridad público ( bicicleta IBike ): base ( bicicleta ) { } cadena de anulación pública GetDetails () => base . GetDetails () + "+ Paquete de seguridad" ; anulación pública doble GetPrice () => base . ObtenerPrecio () + 1 ; } paquete deportivo de clase pública : accesorios para bicicletas { paquete deportivo público ( bicicleta IBike ) : base ( bicicleta ) { } cadena de anulación pública GetDetails () => base . GetDetails () + "+ Paquete deportivo" ; anulación pública doble GetPrice () => base . ObtenerPrecio () + 10 ; } BikeShop de clase pública { public static void UpgradeBike () { var basicBike = new AluminiumBike (); BikeAccessories actualizado = nuevo SportPackage ( basicBike ); actualizado = nuevo paquete de seguridad ( actualizado ); Consola . WriteLine ( $"Bicicleta: '{upgraded.GetDetails()}' Costo: {upgraded.GetPrice()}" ); } }
Producción:
Bicicleta: 'Bicicleta de aluminio + Paquete deportivo + Paquete de seguridad' Costo: 111
clase AbstractCoffee def print pone "Costo: #{ costo } ; Ingredientes: #{ ingredientes } " fin fin clase SimpleCoffee < AbstractCoffee def costo 1 . 0 fin def ingredientes "Café" fin fin clase WithMilk < SimpleDelegator def costo __getobj__ . costo + 0 . 5 final ingredientes def __getobj__ . ingredientes + ", Leche" fin fin clase WithSprinkles < SimpleDelegator def costo __getobj__ . costo + 0 . 2 finales ingredientes def __getobj__ . ingredientes + ", Sprinkles" fin fin café = Café Simple . café nuevo . imprimir cafe = Con Leche . café nuevo ( café ) . imprimir café = Con chispas . café nuevo ( café ) . imprimir
Producción:
Costo: 1,0; Ingredientes: CaféCosto: 1,5; Ingredientes: Café, LecheCosto: 1,7; Ingredientes: Café, Leche, Sprinkles
{{cite book}}
: Mantenimiento CS1: varios nombres: lista de autores ( enlace )