Patrones de diseño estructurales: Decorator

Intensión del patrón
Agregar responsabilidades adicionales a un objeto dinámicamente. El patrón Decorator proporciona una alternativa más flexible que la herencia para extender funcionalidad.
Embellecimiento específico para el cliente de un objeto Core envolviéndolo recursivamente (Wrapping). Como envolver un regalo y ponerlo en una caja. Y finalmente envolver la caja.

Ejemplo de problema
Se quiere agregar nuevo comportamiento o estado a un objeto individual en tiempo de ejecución. La herencia no es factible debido a que es estática y se aplica a toda una clase.


Discusión
Supongamos que trabajamos en una herramienta de interfaz de usuario y deseamos que soporte el agregado de bordes y barras de desplazamiento a la ventana. Podemos definir una jerarquía de herencia como esta:


Pero el patrón Decorator sugiere dar al cliente la habilidad de especificar cualquier combinación de "características" que se desee.

  Widget aWidget = new BorderDecorator(
      new HorizontalScrollBarDecorator(
          new VerticalScrollBarDecorator(
              new Window( 80, 24 ) ) ) );
  aWidget.Draw();

Esta flexibilidad puede lograrse con el siguiente diseño:


Otro ejemplo de características en cascada (o encadenadas) para producir un objeto personalizado, puede lucir así:

  Stream aStream = new CompressingStream(
      new ASCII7Stream(
          new FileStream( "filename.dat" );
  aStream.PutString( "Hello world" );

La solución a este tipo de problemas consiste en la encapsulación del objeto original dentro de una interfaz abstracta tipo Wrapper. Ambos, el objeto Decorator y el objeto Core heredan de la misma interfaz abstracta. La interfaz utiliza composición recursiva para permitir un número ilimitado de capas Decorator que se añaden a cada objeto Core.
Este patrón permite agregar responsabilidades a un objeto, no métodos a la interfaz de un objeto. La interfaz presentada al cliente debe permanecer constante como capas sucesivas sean especificadas.
Además, la identidad del objeto Core ahora está oculta dentro del objeto Decorator. Intentar acceder al objeto Core ahora es un problema.

Estructura
El cliente está siempre interesado en CoreFuncionality.DoThis(). El cliente puede, o no, estar interesado en OptionalOne.DoThis(). Cada una de estas clases siempre delega a la clase base Decorator y dicha clase siempre delega al objeto contenido.


Ejemplo
El Decorator adjunta responsabilidades adicionales a un objeto dinámicamente. Los adornos que son agregados a un pino o abeto son ejemplos de Decorator. Luces, guirnaldas, bolas, la estrella, etc, pueden ser agregados al árbol para darle un aspecto festivo. Los adornos no cambian el árbol en sí mismo que es reconocible como un árbol de navidad, independientemente de los adornos que se hayan usado. Como un ejemplo de funcionalidad adicional, el agregado de luces permite iluminar el árbol de navidad.
Aunque las pinturas se pueden colgar en una pared, con o sin marcos, se agregan a menudo y este es el que en realidad se utiliza para colgar. Antes de ser colgadas, las pinturas deben ser enmarcadas formando un único componente visual.


Check list

  1. Estar seguro de que el contexto es: un único componente principal (o no opcional), una gran cantidad de maquilladores o Wrappers opcionales con una interfaz común a todos.
  2. Crear una interfaz estilo "mínimo denominador común" que haga a todas las clases intercambiables.
  3. Crear una clase base de segundo nivel (Decorator) para soportar las clases Wrappers opcionales.
  4. La clase Core y la clase Decorator implementan la interfaz LCD.
  5. La clase Decorator establece una relación por composición con la interfaz LCD y este miembro es inicializado en el constructor.
  6. La clase Decorator delega en el objeto LCD.
  7. Definir una derivado de la clase Decorator por cada Wrapper opcional.
  8. Dicha clase implementa su propia funcionalidad y delega a la clase Decorator base.
  9. El cliente configura el tipo y el orden de los objetos Core y Decorator.


Reglas de oro

  • El patrón Adapter proporciona una interfaz diferente para su asunto. El patrón Proxy proporciona la misma interfaz. Decorator provee una interfaz mejorada.
  • Adapter cambia la interfaz de un objeto. Decorator mejora las responsabilidades de un objeto. Decorator es más transparente para el cliente. Como consecuencia, Decorator soporta composición recursiva la cual no es posible con Adapters puros.
  • Composite y Decorator poseen un diagrama de estructura similar, reflejando el hecho de que ambos se basan en la composición recursiva para organizar un número abierto de objetos.
  • Decorator puede ser visto como una degeneración de Composite con solo un componente. De cualquier manera, Decorator agrega responsabilidades adicionales, no se pretende para la agregación de objetos.
  • Decorator está diseñado para permitir agregar responsabilidades a un objeto sin usar herencia. El enfoque de Composite no está en la ornamentación de un objeto, sino en su representación.
  • Composite podría utilizar Chain of Responsability para permitir a los componentes acceso global a las propiedades a través de su padre. también podría usar Decorator para sobrescribir esas propiedades en partes de la composición.
  • Decorator y Proxy poseen diferentes propósitos pero estructuras similares. Ambos describen como proporcionar un nivel de indirección a otro objeto y las implementaciones mantienen las referencias al objeto al cual le derivan las solicitudes.
  • Decorator permite cambiar de skin a un objeto. Strategy permite cambiar el interior.


Ejemplo de código en C#
using System;

class MainApp
{
  static void Main()
  {
    // Create ConcreteComponent and two Decorators 
    ConcreteComponent c = new ConcreteComponent();
    ConcreteDecoratorA d1 = new ConcreteDecoratorA();
    ConcreteDecoratorB d2 = new ConcreteDecoratorB();

    // Link decorators 
    d1.SetComponent(c);
    d2.SetComponent(d1);

    d2.Operation();

    // Wait for user 
    Console.Read();
  }
}

// "Component" 
abstract class Component
{
  public abstract void Operation();
}

// "ConcreteComponent" 
class ConcreteComponent : Component
{
  public override void Operation()
  {
    Console.WriteLine("ConcreteComponent.Operation()");
  }
}

// "Decorator" 
abstract class Decorator : Component
{
  protected Component component;

  public void SetComponent(Component component)
  {
    this.component = component;
  }

  public override void Operation()
  {
    if (component != null)
    {
      component.Operation();
    }
  }
}

// "ConcreteDecoratorA" 
class ConcreteDecoratorA : Decorator
{
  private string addedState;

  public override void Operation()
  {
    base.Operation();
    addedState = "New State";
    Console.WriteLine("ConcreteDecoratorA.Operation()");
  }
}

// "ConcreteDecoratorB" 
class ConcreteDecoratorB : Decorator
{
  public override void Operation()
  {
    base.Operation();
    AddedBehavior();
    Console.WriteLine("ConcreteDecoratorB.Operation()");
  }

  void AddedBehavior()
  {
  }
}

0 comentarios:

Publicar un comentario

Muchas gracias por leer el post y comentarlo.

 
Copyright 2009 Programación SOLIDa
BloggerTheme by BloggerThemes | Design by 9thsphere