stringtranslate.com

Patrón de 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 que la funcionalidad de una clase se extienda sin modificarla. [3] 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 decorador [4] es uno de los veintitrés patrones de diseño conocidos ; estos describen cómo resolver problemas de diseño recurrentes y diseñar software orientado a objetos flexible y reutilizable, es decir, objetos que son más fáciles de implementar, cambiar, probar y reutilizar.

¿Qué problemas puede resolver?

Al utilizar subclases, las distintas subclases extienden una clase de distintas maneras. Sin embargo, una extensión está vinculada a la clase en tiempo de compilación y no se puede modificar en tiempo de ejecución. [ cita requerida ]

¿Qué solución describe?

Definir Decoratorobjetos que

Esto permite trabajar con distintos Decoratorobjetos 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 que aparece a continuación.

Intención

Diagrama de clases UML del decorador

El patrón decorador se puede utilizar para extender (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 Decorator que envuelva a la clase original. Esta envoltura se puede lograr mediante la siguiente secuencia de pasos:

  1. Subclasificar la clase Componente original en una clase Decorador (ver diagrama UML);
  2. En la clase Decorator , agregue un puntero de Componente como campo;
  3. En la clase Decorator , pase un Componente al constructor Decorator para inicializar el puntero del Componente ;
  4. En la clase Decorator , reenvía todos los métodos de Component al puntero de Component ; y
  5. En la clase ConcreteDecorator, anule cualquier método de componente cuyo comportamiento necesite modificarse.

Este patrón está diseñado para que se puedan apilar múltiples decoradores uno sobre otro, agregando cada vez una nueva funcionalidad a los métodos reemplazados.

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 en la no decorada.

Las características de decoración (por ejemplo, métodos, propiedades u otros miembros) suelen definirse mediante una interfaz, mixin (también conocido como rasgo ) o herencia de clase que comparten los decoradores y el objeto decorado. En el ejemplo anterior, la clase Component es heredada tanto por ConcreteComponent como por las subclases que descienden de Decorator .

El patrón decorador es una alternativa a la subclasificación . La subclasificación agrega un 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 existen varias formas independientes de extender la funcionalidad. En algunos lenguajes de programación orientados a objetos , las clases no se pueden crear en tiempo de ejecución y, por lo general, no es posible predecir, en tiempo de diseño, qué combinaciones de extensiones serán necesarias. Esto significaría que se tendría que crear una nueva clase para cada combinación posible. Por el contrario, los decoradores son objetos que se crean en tiempo de ejecución y se pueden combinar según el uso. Las implementaciones de flujos de E/S de Java y .NET Framework incorporan el patrón decorador.

Motivación

Diagrama UML para el ejemplo de ventana

Como ejemplo, considere una ventana en un sistema de ventanas . Para permitir el desplazamiento por el contenido de la ventana, se pueden agregar barras de desplazamiento horizontales o verticales , según corresponda. Suponga que las ventanas están representadas por instancias de la interfaz Window y suponga que esta clase no tiene ninguna funcionalidad para agregar barras de desplazamiento. Se podría crear una subclase ScrollingWindow que las proporcione, o crear un ScrollingWindowDecorator que agregue esta funcionalidad a los objetos Window existentes . En este punto, cualquiera de las dos soluciones sería buena.

Ahora, supongamos 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 del decorador, se crea un nuevo BorderedWindowDecorator . Cualquier combinación de ScrollingWindowDecorator o BorderedWindowDecorator puede decorar ventanas existentes. Si la funcionalidad necesita agregarse a todas las ventanas, la clase base puede modificarse. Por otro lado, a veces (por ejemplo, utilizando 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 comunes

Aplicación de decoradores

Agregar o quitar decoradores a pedido (como al 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 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 modificarán su función draw(), lo que hará que se dibujen de manera resaltada (una implementación real probablemente también usaría un sistema de demarcación para maximizar la eficiencia).

La aplicación o eliminación de decoradores en función de los cambios de estado es otro caso de uso común. Según el 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 encapsulen 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 un componente decorado variante que puede compartirse parcialmente o no compartirse por completo. Esta partición del objeto flyweight tiene como objetivo reducir el consumo de memoria. Los decoradores normalmente también se almacenan en caché y se reutilizan. Todos los decoradores contendrán una referencia común al objeto 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. UITableView de iOS implementa el patrón flyweight 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 en la interacción con los decoradores

La aplicación de combinaciones de decoradores de diversas formas a una colección de objetos presenta algunos problemas a la hora de interactuar con la colección de una manera que aproveche al máximo la funcionalidad añadida por los decoradores. El uso de patrones de adaptador o visitante puede resultar útil en estos casos. La interacción con varias capas de decoradores plantea desafíos adicionales y la lógica de los adaptadores y visitantes debe diseñarse para tenerlos en cuenta.

Relevancia arquitectónica

Los decoradores permiten un enfoque compositivo en lugar de uno jerárquico descendente para ampliar la funcionalidad. Un decorador permite añadir o modificar el comportamiento de una interfaz en tiempo de ejecución. Se pueden utilizar para encapsular objetos en una combinación arbitraria de varias capas. Hacer lo mismo con las subclases significa implementar redes complejas de herencia múltiple, lo que es ineficiente en términos de memoria y, en un momento determinado, simplemente no se puede escalar. Del mismo modo, intentar implementar la misma funcionalidad con propiedades sobrecarga cada instancia del objeto con propiedades innecesarias. Por las razones anteriores, los decoradores suelen considerarse una alternativa eficiente en términos de memoria a la subclasificación.

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 de decorador también puede ampliar el patrón de fachada . Una fachada está diseñada para interactuar simplemente con el sistema complejo que encapsula, pero no agrega funcionalidad al sistema. Sin embargo, la envoltura de un sistema complejo proporciona un espacio que puede usarse para introducir nueva funcionalidad basada en la coordinación de subcomponentes en el sistema. Por ejemplo, un patrón de fachada puede unificar muchos diccionarios de diferentes idiomas bajo una interfaz de diccionario multilingüe. La nueva interfaz también puede proporcionar nuevas funciones para traducir palabras entre idiomas. Este es un patrón híbrido: la interfaz unificada proporciona un espacio para la ampliación. Piense en los decoradores como si no se limitaran a envolver objetos individuales, sino que también fueran capaces de envolver grupos de objetos en este enfoque híbrido.

Alternativas a los decoradores

Como alternativa al patrón decorador, se puede utilizar el adaptador cuando el contenedor 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 con un objeto subyacente. [6]

Estructura

Diagrama de clases y secuencias UML

Un ejemplo de diagrama de clase y secuencia UML para el patrón de diseño Decorador. [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 hace que sea transparente (invisible) para los clientes de .DecoratorcomponentComponentcomponent.operation()DecoratorComponent

Las subclases ( Decorator1, Decorator2) implementan un comportamiento adicional ( addBehavior()) que se debe agregar a 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 extender la funcionalidad de un Component1objeto.
Las Clientllamadas operation() a Decorator1, que reenvía la solicitud a Decorator2. Decorator2se ejecuta addBehavior()después de reenviar la solicitud a Component1y regresa a Decorator1, que se ejecuta addBehavior() y regresa a Client.

Ejemplos

C++

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

#include <iostream> #include <memoria>  // Interfaz de bebidas. clase Bebida { público : virtual void bebida () = 0 ; virtual ~ Bebida () = predeterminado ; };           // Bebidas que se pueden decorar. clase Café : público Bebida { público : virtual void bebida () override { std :: cout << "Bebiendo Café" ; } };              clase Soda : pública Bebida { pública : virtual void bebida () anular { std :: cout << "Bebiendo Soda" ; } };              clase BeverageDecorator : público Beverage { público : BeverageDecorator () = eliminar ; BeverageDecorator ( std :: unique_ptr < Beverage > componente_ ) : componente ( std :: move ( componente_ )) {} virtual void bebida () = 0 ;                     protegido : void callComponentDrink () { if ( componente ) { componente -> bebida (); } }         privado : std :: unique_ptr < Bebida > componente ; };  clase Leche : público BeverageDecorator { público : Leche ( std :: unique_ptr < Bebida > componente_ , float porcentaje_ ) : BebidaDecorator ( std :: move ( componente_ )) , porcentaje ( porcentaje_ ) { }                   virtual void beber () anular { callComponentDrink (); std :: cout << ", con leche de riqueza " << porcentaje << "%" ; }             privado : porcentaje flotante ; }; clase IceCubes : public BeverageDecorator { public : IceCubes ( std :: unique_ptr < Beverage > componente_ , int count_ ) : BeverageDecorator ( std :: move ( componente_ )) , count ( count_ ) { }                   virtual void bebida () anular { callComponentDrink (); std :: cout << ", con " << count << " cubitos de hielo" ; }             privado : int cuenta ; }; clase Azúcar : público BeverageDecorator { público : Azúcar ( std :: unique_ptr < Bebida > componente_ , int cucharas_ ) : BeverageDecorator ( std :: move ( componente_ )) , cucharas ( cucharas_ ) { }                   virtual void bebida () anular { callComponentDrink (); std :: cout << ", con " << cucharas << " cucharadas de azúcar" ; }             privado : int cucharas = 1 ; };   int main () { std :: unique_ptr < Bebida > soda = std :: make_unique < Soda > (); soda = std :: make_unique < CubitosDeHielo > ( std :: move ( soda ), 3 ); soda = std :: make_unique < Azúcar > ( std :: move ( soda ), 1 );                refresco -> bebida (); std :: cout << std :: endl ; std :: unique_ptr < Bebida > café = std :: make_unique < Café > (); café = std :: make_unique < CubitosDeHielo > ( std :: move ( café ), 16 ); café = std :: make_unique < Leche > ( std :: move ( café ), 3. ); café = std :: make_unique < Azúcar > ( std :: move ( café ), 2 );                     café -> bebida (); devuelve 0 ; } 

La salida del programa es como

Beber refresco , 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, componible en tiempo de ejecución (tiene problemas al llamar a funciones decoradas a menos que se utilice un proxy explícito) y un decorador que usa herencia de mixin.

Decorador dinámico

#include <iostream> #include <cadena>  estructura Forma { virtual ~ Forma () = predeterminado ;       virtual std :: cadena GetName () const = 0 ; };     estructura Círculo : Forma { void Redimensionar ( float factor ) { radio *= factor ; }             std :: string GetName () const override { return std :: string ( "Un círculo de radio" ) + std :: to_string ( radio ); }          radio flotante = 10.0f ; };   struct ColoredShape : Forma { ColoredShape ( const std :: string & color , Forma * forma ) : color ( color ), forma ( forma ) {}              std :: string GetName () const override { return forma -> GetName () + "que es coloreado" + color ; }            std :: cadena color ; Forma * forma ; };   int main () { Círculo círculo ; FormaColoreada forma_coloreada ( " rojo" , & círculo ); std :: cout << forma_coloreada . ObtenerNombre () << std :: endl ;            }
#include <memoria> #include <iostream> #include <cadena>   estructura WebPage { virtual void display () = 0 ; virtual ~ WebPage () = predeterminado ; };        struct BasicWebPage : WebPage { std :: string html ; void display () override { std :: cout << "Página WEB básica" << std :: endl ; } };               estructura WebPageDecorator : WebPage { WebPageDecorator ( std :: unique_ptr < WebPage > webPage ) : _webPage ( std :: move ( webPage )) { } void display () override { _webPage -> display (); } privado : std :: unique_ptr < WebPage > _webPage ; };                estructura PáginaWebAutenticada : DecoradorDePáginaWeb { PáginaWebAutenticada ( std :: unique_ptr < PáginaWeb > PáginaWeb ) : DecoradorDePáginaWeb ( std :: mover ( PáginaWeb )) {}         void authenticateUser () { std :: cout << "autenticación realizada" << std :: endl ; } void display () override { authenticateUser (); WebPageDecorator :: display (); } };               estructura AuthorizedWebPage : WebPageDecorator { AuthorizedWebPage ( std :: unique_ptr < WebPage > webPage ) : WebPageDecorator ( std :: move ( webPage )) {}         void usuarioautorizado () { std :: cout << "autorizado hecho" << std :: endl ; } void display () override { usuarioautorizado (); WebPageDecorator :: display (); } };               int principal ( int argc , char * argv []) { std :: unique_ptr < PáginaWeb > miPagina = std :: make_unique < PáginaWebBásica > ();         miPagina = std :: make_unique <PaginaWebAutorizada> ( std :: mover ( miPagina )); miPagina = std :: make_unique <PaginaWebAutenticada> ( std :: mover ( miPagina ) ) ; miPagina - > mostrar () ; std :: cout << std :: endl ; devolver 0 ; }           

Decorador estático (herencia de Mixin)

Este ejemplo demuestra una implementación de Decorator estático, lo cual es posible gracias a la capacidad de C++ de heredar del argumento de plantilla.

#include <iostream> #include <cadena>  estructura Círculo { void Redimensionar ( float factor ) { radio *= factor ; }           std :: string GetName () const { return std :: string ( "Un círculo de radio" ) + std :: to_string ( radio ); }         radio flotante = 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 :: cadena color ; }; int main () { FormaColoreada < Círculo > círculo_rojo ( "rojo" ); std :: cout << círculo_rojo . ObtenerNombre () << std :: endl ; círculo_rojo . Redimensionar ( 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 la ventana public interface Window { void draw (); // Dibuja la cadena Window getDescription (); // Devuelve una descripción de la ventana }         // Implementación de una ventana simple sin ninguna barra de desplazamiento clase  SimpleWindow implementa Window { @Override public void draw () { // Dibujar ventana } @Override public String getDescription () { return "ventana simple" ; } }                  

Las siguientes clases contienen los decoradores para todas Windowlas clases, incluidas las propias clases decoradoras.

// clase decoradora abstracta - tenga en cuenta que implementa Window clase abstracta WindowDecorator implementa Window { private final Window windowToBeDecorated ; // la ventana que se está decorando           public WindowDecorator ( Window ventanaASerDecorado ) { this . ventanaASerDecorado = ventanaASerDecorado ; } @Override public void draw () { ventanaASerDecorado . draw (); //Delegación } @Override public String getDescription () { return ventanaASerDecorado . getDescription (); //Delegación } }                         // El primer decorador concreto que agrega la funcionalidad de barra de desplazamiento vertical class  VerticalScrollBarDecorator extends WindowDecorator { public VerticalScrollBarDecorator ( Window windowToBeDecorated ) { super ( windowToBeDecorated ); }           @Override public void draw () { super.draw (); 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 concreto que agrega la funcionalidad de barra de desplazamiento horizontal class  HorizontalScrollBarDecorator extends WindowDecorator { public HorizontalScrollBarDecorator ( Window windowToBeDecorated ) { super ( windowToBeDecorated ); }           @Override public void draw () { super.draw (); 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 Windowinstancia completamente decorada (es decir, con barras de desplazamiento verticales y horizontales) e imprime su descripción:

clase pública DecoratedWindowTest { pública estática void main ( String [] args ) { // Crea una ventana decorada con barras de desplazamiento horizontales y verticales Ventana decoradaWindow = new HorizontalScrollBarDecorator ( new VerticalScrollBarDecorator ( new SimpleWindow ()));                     // Imprimir la descripción de la ventana System . out . println ( decorativeWindow . getDescription ()); } }  

El resultado de este programa es "ventana simple, incluidas las barras de desplazamiento verticales, incluidas las 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 el desarrollo impulsado por pruebas

importar org.junit.Assert.assertEquals estático ; importar org.junit.Test ; public class WindowDecoratorTest { @Test public void testWindowDecoratorTest () { Ventana decoradaWindow = new HorizontalScrollBarDecorator ( new VerticalScrollBarDecorator ( new SimpleWindow ())); // afirmar que la descripción de hecho incluye barras de desplazamiento horizontales y verticales assertEquals ( "ventana simple, incluidas las barras de desplazamiento verticales, incluidas las barras de desplazamiento horizontales" , decoradaWindow . getDescription ()); } }                


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

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

// La interfaz Coffee define la funcionalidad de Coffee implementada por el decorador public interface Coffee { public double getCost (); // Devuelve el costo del café public String getIngredients (); // Devuelve los ingredientes del café }           // Extensión de un café simple sin ningún ingrediente extra 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 clases de decoradores en sí.

// Clase decoradora abstracta: tenga en cuenta que implementa la interfaz Coffee clase abstracta pública CoffeeDecorator implementa Coffee { private final Coffee decoradoCoffee ;           public CoffeeDecorator ( Café c ) { this.decoratedCoffee = c ; }        @Override public double getCost () { // Implementando los métodos de la interfaz return decorativeCoffee.getCost ( ) ; }         @Override public String obtenerIngredientes () { return Cafédecorado.obtenerIngredientes ( ) ; } }       // Decorator WithMilk mezcla leche con café. // Tenga en cuenta que extiende CoffeeDecorator. class  WithMilk extends CoffeeDecorator { public WithMilk ( Coffee c ) { super ( c ); }          @Override public double getCost () { // Anulando los métodos definidos en la superclase abstracta return super . getCost () + 0.5 ; }           @Override public String obtenerIngredientes () { return super.getIngredientes ( ) + " , Leche" ; } }         // Decorator WithSprinkles mezcla chispas sobre el café. // Tenga en cuenta que extiende CoffeeDecorator. class  WithSprinkles extiende CoffeeDecorator { public WithSprinkles ( Coffee c ) { super ( c ); }          @Override public double obtenerCost ( ) { devolver super.getCost ( ) + 0.2 ; }          @Override public String obtenerIngredientes () { return super.getIngredientes () + " , Chispas" ; } }         

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 Main { public static void printInfo ( Café c ) { System.out.println ( " Costo: " + c.getCost ( ) + " ; Ingredientes : " + c.getIngredients ( ) ) ; }                  public static void main ( String [] args ) { Café c = new SimpleCoffee (); printInfo ( c );            c = nuevo WithMilk ( c ); printInfo ( c );     c = nuevo WithSprinkles ( c ); printInfo ( c ); } }     

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

Costo: 1.0; Ingredientes: CaféCosto: 1.5; Ingredientes: Café, LecheCosto: 1.7; Ingredientes: Café, Leche, Chispas

PHP

 clase  abstracta Componente {  protegido  $datos ;  protegido  $valor ;  función pública  abstracta getData ();   función pública  abstracta getValue (); } clase  ConcreteComponent  extiende  Component {  función pública  __construct () { $this -> valor = 1000 ; $this -> datos = "Componente de hormigón: \t { $this -> valor } \n " ; }           función  pública getData ()  {  devolver  $this -> datos ;  }  función  pública getValue ()  {  return  $this -> valor ;  } } La clase  abstracta Decorator  extiende  Component { }clase  ConcreteDecorator1  extiende  Decorator {  función pública  __construct ( Componente $data ) { $this -> valor = 500 ; $this -> datos = $data ; }            función  pública obtenerDatos ()  {  devolver  $this -> datos -> obtenerDatos ()  .  "Decorador de hormigón 1: \t { $this -> valor } \n " ;  }  función  pública obtenerValor ()  {  devolver  $this -> valor  +  $this -> datos -> obtenerValor ();  } }clase  ConcreteDecorator2  extiende  Decorator {  función pública  __construct ( Componente $data ) { $this -> valor = 500 ; $this -> datos = $data ; }            función  pública obtenerDatos ()  {  devolver  $this -> datos -> obtenerDatos ()  .  "Decorador de hormigón 2: \t { $this -> valor } \n " ;  }  función  pública obtenerValor ()  {  devolver  $this -> valor  +  $this -> datos -> obtenerValor ();  } }clase  Cliente {  privado  $componente ;  función  pública __construct ()  {  $this -> componente  =  new  ConcreteComponent ();  $this -> componente  =  $this -> wrapComponent ( $this -> componente ); echo  $this -> componente -> obtenerDatos ();  echo  "Cliente: \t\t\t " ;  echo  $this -> componente -> obtenerValor ();  }  función  privada wrapComponent ( Componente  $componente )  {  $componente1  =  nuevo  ConcreteDecorator1 ( $componente );  $componente2  =  nuevo  ConcreteDecorator2 ( $componente1 );  devolver  $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 con valores de 0 a 255. """importar  aleatoriodef  s32_to_u16 ( x ):  si  x  <  0 :  signo  =  0xF000  de lo contrario :  signo  =  0  inferior  =  x  &  0x00007FFF  devolver  inferior  |  signodef  semilla_de_xy ( x ,  y ):  devuelve  s32_a_u16 ( x )  |  ( s32_a_u16 ( y )  <<  16 )clase  RandomSquare :  def  __init__ ( s ,  modificador_semilla ):  s . modificador_semilla  =  modificador_semilla def  get ( s ,  x ,  y ) :  semilla  =  semilla_de_xy ( x ,  y )  ^  s.semilla_modificador aleatorio.semilla ( semilla ) return aleatorio.randint (  0,255 )   clase  DataSquare :  def  __init __ ( s ,  valor_inicial = None )  : s.data = [ valor_inicial ] * 10 * 10       def  get ( s ,  x ,  y ):  return  s . data [( y  *  10 )  +  x ]  # sí: todos son 10x10 def  conjunto ( s ,  x ,  y ,  u ):  s . datos [( y  *  10 )  +  x ]  =  uclase  CacheDecorator :  def  __init__ ( s ,  decorado ):  s . decorado  =  decorado  s . cache  =  DataSquare () def  obtener ( s ,  x ,  y ):  si  s . cache . get ( x ,  y )  ==  Ninguno :  s . cache . set ( x ,  y ,  s . decorado . get ( x ,  y ))  devuelve  s . cache . get ( x ,  y )clase  MaxDecorator :  def  __init__ ( s ,  decorado ,  max ):  s . decorado  =  decorado  s . max  =  max def  get ( s ,  x ,  y ) :  if  s.decorated.get ( x , y ) > s.max : return s.max return s.decorated.get ( x , y )        clase  MinDecorator :  def  __init__ ( s ,  decorado ,  min ):  s . decorado  =  decorado  s . min  =  min def  get ( s ,  x ,  y ) :  if  s.decorated.get ( x , y ) < s.min : return s.min return s.decorated.get ( x , y )        clase  VisibilityDecorator :  def  __init__ ( s ,  decorado ):  s . decorado  =  decorado def  get ( s ,  x ,  y ):  devuelve  s . decorado . get ( x ,  y ) def  draw ( s ):  para  y  en  rango ( 10 ):  para  x  en  rango ( 10 ):  imprimir  " %3d "  %  s . get ( x ,  y ),  imprimir# Ahora, crea una tubería de decoradores:cuadrado_aleatorio  =  Cuadrado_aleatorio ( 635 ) caché_aleatorio  =  Decorador_de_caché ( cuadrado_aleatorio ) filtrado_máximo  =  Decorador_máximo ( caché_aleatorio ,  200 ) filtrado_mínimo  =  Decorador_mínimo ( filtrado_máximo ,  100 ) final  =  Decorador_de_visibilidad ( filtrado_mínimo )sorteo final ( )

Nota:

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

En segundo lugar, en la Wiki de Python:

El patrón Decorator es un patrón descrito en el libro Design Patterns Book. Es una forma de modificar aparentemente el comportamiento de un objeto, encerrándolo dentro de un objeto decorativo con una interfaz similar. No debe confundirse con los Decorators de Python, que es una característica del lenguaje para modificar dinámicamente una función o clase. [8]

Cristal

Clase abstracta Café Definición abstracta Costo Definición abstracta Ingredientes Fin        # Extensión de una clase de café simple SimpleCoffee < Coffee def cost 1 . 0 end        def ingredientes "Café" fin fin   # Clase decoradora abstracta CoffeeDecorator < Coffee protected getter decorative_coffee : Coffee         def inicializar ( @decorated_coffee ) fin   def costo café decorado . costo fin    def ingredientes café decorado . ingredientes fin fin   clase WithMilk < CoffeeDecorator def costo super + 0 . 5 fin          def ingredientes super + ", leche" fin fin     clase WithSprinkles < CoffeeDecorator def costo super + 0 . 2 fin          def ingredientes super + ", chispas" fin fin     clase Programa def print ( café : Café ) pone " Costo: #{ café.costo } ; Ingredientes : # { café.ingredientes } " fin         def inicializar café = SimpleCoffee . new print ( café )      café = ConLeche . nuevo ( café ) imprimir ( café )    café = WithSprinkles . 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, Chispas

DO#

espacio de nombres WikiDesignPatterns ; interfaz pública IBike { cadena GetDetails (); doble GetPrice (); }      clase pública AluminiumBike : IBike { pública doble GetPrice () => 100.0 ;          public string GetDetails () => "Bicicleta de aluminio" ; }    clase pública CarbonBike : IBike { pública doble GetPrice () => 1000.0 ;          cadena pública GetDetails () => "Carbono" ; }    clase abstracta pública BikeAccessories : IBike { privada de solo lectura IBike _bike ;          AccesoriosBici públicos ( IBike bicicleta ) { _bike = bicicleta ; }        público virtual doble ObtenerPrecio () => _bike.ObtenerPrecio ( ) ;      cadena virtual pública GetDetails () => _bike.GetDetails ( ) ; }     clase pública SecurityPackage : BikeAccessories { paquete público SecurityPackage ( IBike bicicleta ): base ( bicicleta ) {         } cadena de anulación pública GetDetails () => base . GetDetails () + " + Paquete de seguridad" ;        público anular doble ObtenerPrecio ( ) => base.ObtenerPrecio ( ) + 1 ; }       clase pública SportPackage : BikeAccessories { clase pública SportPackage ( IBike bicicleta ) : base ( bicicleta ) {           } cadena de anulación pública GetDetails () => base . GetDetails () + " + Paquete deportivo" ;        público anular doble ObtenerPrecio ( ) => base.ObtenerPrecio ( ) + 10 ; }       clase pública BikeShop { pública estática void UpgradeBike () { var basicBike = new AluminiumBike (); BikeAccessories actualizado = nuevo SportPackage ( basicBike ); actualizado = nuevo SecurityPackage ( actualizado );                      Consola .WriteLine ( $"Bicicleta: '{upgraded.GetDetails()}' Costo: {upgraded.GetPrice()}" ) ; } }

Producción:

Bicicleta: 'Bicicleta de Aluminio + Paquete Deportivo + Paquete de Seguridad' Coste: 111


Rubí

clase AbstractCoffee def print puts "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 fin          def ingredientes __getobj__ . ingredientes + ", Leche" fin fin     clase WithSprinkles < SimpleDelegator def costo __getobj__ . costo + 0 . 2 fin          def ingredientes __getobj__ . ingredientes + ", Chispas" fin fin     café = SimpleCoffee . nuevo café . imprimir  café = ConLeche . nuevo ( café ) café . imprimir  café = WithSprinkles . nuevo ( café ) café . imprimir  


Producción:

Costo: 1.0; Ingredientes: CaféCosto: 1.5; Ingredientes: Café, LecheCosto: 1.7; Ingredientes: Café, Leche, Chispas

Véase también

Referencias

  1. ^ 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.
  2. ^ "Cómo implementar un patrón de decoración". Archivado desde el original el 7 de julio de 2015.
  3. ^ "El patrón Decorator, 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. pp. 175ff. ISBN 0-201-63361-2.{{cite book}}: CS1 maint: multiple names: authors list (link)
  5. ^ "El patrón de diseño Decorator: problema, solución y aplicabilidad". w3sDesign.com . Consultado el 12 de agosto de 2017 .
  6. ^ Freeman, Eric; Freeman, 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. Recuperado el 2 de julio de 2012 .
  7. ^ "El patrón de diseño Decorator - Estructura y colaboración". w3sDesign.com . Consultado el 12 de agosto de 2017 .
  8. ^ "Patrón Decorador - Wiki de Python". wiki.python.org .

Enlaces externos