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?
- Las responsabilidades se deben agregar (y eliminar) a un objeto dinámicamente en tiempo de ejecución. [5]
- Se debería proporcionar una alternativa flexible a la subclasificación para ampliar la funcionalidad.
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
- Implementar la interfaz del objeto extendido (decorado) (
Component) de forma transparente reenviándole todas las solicitudes. - realizar una funcionalidad adicional antes/después de reenviar una solicitud.
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

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

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

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é, Leche Costo: 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 aleatorio
def s32_to_u16 ( x ):
si x < 0 :
signo = 0xF000
de lo contrario :
signo = 0
inferior = x & 0x00007FFF
devolver inferior | signo
def 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 ] = u
clase 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é, Leche Costo: 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é, Leche Costo: 1.7; Ingredientes: Café, Leche, Chispas
Véase también
- Patrón compuesto
- Patrón adaptador
- Clase abstracta
- Fábrica abstracta
- Programación orientada a aspectos
- Objeto inmutable
Referencias
- ^ Gamma, Erich; et al. (1995). Patrones de diseño. Reading, MA: Addison-Wesley Publishing Co, Inc., págs. 175 y siguientes. ISBN 0-201-63361-2.
- ^ "Cómo implementar un patrón de decoración". Archivado desde el original el 7 de julio de 2015.
- ^ "El patrón Decorator, por qué dejamos de usarlo y la alternativa". 8 de marzo de 2022.
- ^ 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) - ^ "El patrón de diseño Decorator: problema, solución y aplicabilidad". w3sDesign.com . Consultado el 12 de agosto de 2017 .
- ^ Freeman, Eric; Freeman, Elisabeth; Sierra, Kathy; Bates, Bert (2004). Hendrickson, Mike; Loukides, Mike (eds.). 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 .
- ^ "El patrón de diseño Decorator - Estructura y colaboración". w3sDesign.com . Consultado el 12 de agosto de 2017 .
- ^ "Patrón Decorador - Wiki de Python". wiki.python.org .
Enlaces externos
- Implementación del patrón Decorador en Java
- Descripción del patrón de decorador del Repositorio de patrones de Portland
