stringtranslate.com

Patrón decorador

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.

Descripción general

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.

¿Qué problemas puede resolver?

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 ]

¿Qué solución describe?

Definir Decoratorobjetos que

Esto permite trabajar con diferentes Decoratorobjetos 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.

Intención

Diagrama de clases UML decorador

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:

  1. Subclasificar la clase Componente original en una clase Decorador (ver diagrama UML);
  2. En la clase Decorador , agregue un puntero de Componente como campo;
  3. En la clase Decorador , pase un Componente al constructor Decorador para inicializar el puntero del Componente ;
  4. En la clase Decorador , reenvíe todos los métodos de Componente al puntero de Componente ; y
  5. 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 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.

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, 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.

Casos de uso comunes

Aplicando decoradores

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.

Uso en objetos Flyweight

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.

Obstáculos al interactuar con decoradores.

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.

Relevancia arquitectónica

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.

Uso para mejorar las API

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.

Alternativas a los decoradores

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]

Estructura

Diagrama de secuencia y clase UML

Un ejemplo de diagrama de secuencia y clases UML para el patrón de diseño Decorator. [7]

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 .DecoratorcomponentComponentcomponent.operation()DecoratorComponent

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 Clientobjeto funciona a través de objetos Decorator1y Decorator2para ampliar la funcionalidad de un Component1objeto.
Las Clientllamadas operation() a Decorator1, que reenvían la solicitud a Decorator2. Decorator2realiza addBehavior()después de reenviar la solicitud Component1y regresa a Decorator1, que realiza addBehavior() y regresa a Client.

Ejemplos

C++

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.

C++

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.

Decorador dinámico

#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 ; }           

Decorador estático (herencia Mixin)

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 ; }               

Java

Primer ejemplo (escenario de ventana/desplazamiento)

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 Windowlas 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 Windowinstancia 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 getDescriptionmétodo de los dos decoradores primero recupera la Windowdescripció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 ()); } }                


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

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

PHP

 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

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 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]

Cristal

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

C#

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


Rubí

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

Ver también

Referencias

  1. ^ Gama, 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.
  2. ^ "Cómo implementar un patrón decorador". Archivado desde el original el 7 de julio de 2015.
  3. ^ "El patrón decorador, por qué dejamos de usarlo y la alternativa". 8 de marzo de 2022.
  4. ^ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Patrones de diseño: elementos de software reutilizable orientado a objetos. Addison Wesley. págs. 175 y siguientes. ISBN 0-201-63361-2.{{cite book}}: Mantenimiento CS1: varios nombres: lista de autores ( enlace )
  5. ^ "El patrón de diseño Decorator: problema, solución y aplicabilidad". w3sDesign.com . Consultado el 12 de agosto de 2017 .
  6. ^ Hombre libre, Eric; Hombre libre, Elisabeth; Sierra, Kathy; Bates, Bert (2004). Hendrickson, Mike; Loukides, Mike (eds.). Patrones de diseño Head First (libro de bolsillo) . vol. 1. O'Reilly. págs.243, 252, 258, 260. ISBN 978-0-596-00712-6. Consultado el 2 de julio de 2012 .
  7. ^ "El patrón de diseño Decorador: estructura y colaboración". w3sDesign.com . Consultado el 12 de agosto de 2017 .
  8. ^ "DecoratorPattern - Wiki de Python". wiki.python.org .

enlaces externos