Patrones de diseño estructurales: Bridge

Intención del patrón
  • Desacoplar una abstracción de su implementación de manera que ambas puedan variar independientemente.
  • Publicar la interfaz en una jerarquía de herencias y ocultar la implementación en su propia jerarquía de herencia.
  • Además de encapsulación, se usa para aislamiento.

Ejemplo de problema
"El endurecimiento de las arterias del software" ha ocurrido por el uso de subclases de una clase base abstracta para proporcionar implementaciones alternativas. Esto "suelda", en tiempo de compilación, la unión entre la interfaz y la implementación. La abstracción y la implementación no pueden ser extendidas o compuestas de manera independiente.

Motivación
Consideremos el dominio de "planificación de hilos" (Thread scheduling)


Existen dos tipos de planificadores de hilos y dos tipos de sistemas operativos o plataformas. Dado este enfoque de especialización, se debe definir una clase por cada permutación de estas dos dimensiones. Si añadimos una nueva plataforma, la jerarquía de clases queda así:


¿Qué tal si tuviéramos tres tipos de planificadores de hilos y cuatro tipos de plataformas? ¿Qué tal si tuviéramos cinco tipos de planificadores y diez tipos de plataformas? El número de clases que deberíamos definir es el producto del número de planificadores de hilos y el número de plataformas.
El patrón de diseño Bridge propone refactorizar esta jerarquía de herencias exponencial y explosiva en dos jerarquías ortogonales. Una para las abstracciones de plataformas independientes y otra para las implementaciones de plataformas dependientes.


Discusión
Al descomponer la interfaz del componente y la implementación en jerarquías de clases ortogonales. La clase de interfaz contiene una referencia a la clase de implementación abstracta. Esta referencia es inicializada con una instancia de una clase concreta de implementación, pero toda la interacción posterior entre la clase de interfaz y la clase de implementación está limitada a la abstracción mantenida en la clase base de la implementación. El cliente interactúa con la clase de interfaz, y ella a su vez "delega" todas las solicitudes a la clase de implementación.
El objeto de interfaz es el "handle" conocido y usado por el cliente; mientras que el objeto de implementación, o "body", se encapsula de forma segura para asegurarse de que puede seguir evolucionando, o ser enteramente reemplazado (o compartido) en tiempo de ejecución.
Se debe usar el patrón de diseño Bridge cuando:
  • Se quiere un enlace de la implementación en tiempo de ejecución.
  • Se posee una proliferación de clases resultado del acoplamiento de la interfaz y numerosas implementaciones.
  • Se desea compartir una implementación entre múltiples objetos.
  • Se necesita asignar jerarquías de clase ortogonales.
Consecuencias:
  • Desacoplar la interfaces del objeto.
  • Extensibilidad mejorada. Se puede extender (por ej. Subclase) las jerarquías de abstracción y de implementación independientemente.
  • Ocultar detalles a los clientes.
Bridge es un sinónimo para el patrón "handle/body". Este es un mecanismo de diseño que encapsula una clase de implementación dentro de una clase de interfaz. El primero es el body y el segundo es el handle. El handle es visto por el cliente como la clase real, pero el trabajo es llevado a cabo en el body. Este patrón puede ser usado para descomponer una abstracción compleja en clases más pequeñas y manejables. También puede ser usado para compartir un único recurso entre múltiples clases que lo controlan.

Estructura
El cliente no desea lidiar con los detalles de la dependencia de plataformas. El patrón Bridge encapsula esta complejidad detrás de una abstracción "wrapper".
El patrón de diseño Bridge enfatiza la identificación y desacople entre la abstracción de "interfaz" y la abstracción de "implementación".


Ejemplo
El patrón Bridge desacopla una abstracción de su implementación de modo que los dos puedan variar independientemente. Un interruptor de control de luces, ventiladores de techo, etc. de una casa, es un ejemplo de Bridge. El propósito del interruptor es encender o apagar un dispositivo. Este interruptor puede ser implementado como una cadena de tracción, un interruptor simple de dos posiciones o como una variedad de interruptor dimmer.


Check list
  1. Decidir si dos dimensiones ortogonales existen en el dominio. Estos conceptos independientes pueden ser: abstracción/plataforma, dominio/infraestructura, front-end/back-end o interfaz/implementación.
  2. Diseñar la separación de intereses: qué quiere el cliente y qué proporciona la plataforma.
  3. Diseñar una interfaz orientada a la plataforma que sea mínima, necesaria y suficiente. Su objetivo es desacoplar la abstracción de la plataforma.
  4. Definir una clase derivada de esa interfaz por cada una de esas plataformas.
  5. Crear la clase base de abstracción que "tiene un" objeto plataforma y delegarle la funcionalidad orientada a la plataforma a ella.
  6. Definir especializaciones de la clase abstracción.

Reglas de oro

  • El patrón Adapter hace que las cosas funcionen luego de haber sido diseñadas. por otro lado el patrón Bridge hace que funcionen antes del diseño.
  • El patrón Bridge está diseñado por adelantado para que la abstracción y la implementación varíen independientemente mientras que Adapter es retroadaptado para que las clases puedan trabajar en forma conjunta.
  • Los patrones State, Strategy, Bridge (y hasta cierto grado el Adapter) tienen una estructura similar de solución. Todos ellos comparten elementos del patrón "handle/body". Pero se diferencian en la intención, es decir, resolver problemas diferentes.
  • La estructura de los patrones State y Bridge, son idénticas (Con la excepción que Bridge admite jerarquías de clase de las clases handle, mientras que el patrón de diseño State admite sólo una). Los dos patrones usan la misma estructura para resolver distintos problemas: State le permite a un objeto cambiar su comportamiento junto con su estado, mientras que la intención del patrón Bridge es desacoplar una abstracción de su implementación para que ambas puedan variar independientemente.
  • Si las clases de interfaz delegan su creación de sus clases de implementación (en lugar de crearse/acoplarse a ellas mismas directamente) entonces el diseño por lo general usa el patrón de diseño Abstract Factory para crear los objetos de implementación.


Ejemplo en código C#
using System;

class MainApp
{
  static void Main()
  {
    Abstraction ab = new RefinedAbstraction();

    // Set implementation and call 
    ab.Implementor = new ConcreteImplementorA();
    ab.Operation();

    // Change implemention and call 
    ab.Implementor = new ConcreteImplementorB();
    ab.Operation();

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

// "Abstraction" 
class Abstraction
{
  protected Implementor implementor;

  // Property 
  public Implementor Implementor
  {
    set{ implementor = value; }
  }

  public virtual void Operation()
  {
    implementor.Operation();
  }
}

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

// "RefinedAbstraction" 
class RefinedAbstraction : Abstraction
{
  public override void Operation()
  {
    implementor.Operation();
  }
}

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

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

0 comentarios:

Publicar un comentario

Muchas gracias por leer el post y comentarlo.

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