No entiendo el patrón de diseño "puente" en absoluto. He visitado varios sitios web, pero no me han ayudado.
¿Alguien puede ayudarme a entender esto?
No entiendo el patrón de diseño "puente" en absoluto. He visitado varios sitios web, pero no me han ayudado.
¿Alguien puede ayudarme a entender esto?
Respuestas:
En OOP usamos polimorfismo para que una abstracción pueda tener múltiples implementaciones. Veamos el siguiente ejemplo:
//trains abstraction
public interface Train
{
move();
}
public class MonoRail:Train
{
public override move()
{
//use one track;
}
}
public class Rail:Train
{
public override move()
{
//use two tracks;
}
}
Se introdujo un nuevo requisito y debe incorporar la perspectiva de aceleración de los trenes, por lo tanto, cambie el código como se muestra a continuación.
public interface Train
{
void move();
}
public class MonoRail:Train
{
public override void move()
{
//use one track;
}
}
public class ElectricMonoRail:MonoRail
{
public override void move()
{
//use electric engine on one track.
}
}
public class DieselMonoRail: MonoRail
{
public override void move()
{
//use diesel engine on one track.
}
}
public class Rail:Train
{
public override void move()
{
//use two tracks;
}
}
public class ElectricRail:Rail
{
public override void move()
{
//use electric engine on two tracks.
}
}
public class DieselRail: Rail
{
public override void move()
{
//use diesel engine on two tracks.
}
}
El código anterior no es mantenible y carece de reutilización (suponiendo que podamos reutilizar el mecanismo de aceleración para la misma plataforma de seguimiento). El siguiente código aplica el patrón del puente y separa las dos abstracciones diferentes, el transporte del tren y la aceleración .
public interface Train
{
void move(Accelerable engine);
}
public interface Accelerable
{
public void accelerate();
}
public class MonoRail:Train
{
public override void move(Accelerable engine)
{
//use one track;
engine.accelerate(); //engine is pluggable (runtime dynamic)
}
}
public class Rail:Train
{
public override void move(Accelerable engine)
{
//use two tracks;
engine.accelerate(); //engine is pluggable (runtime dynamic)
}
}
public class ElectricEngine:Accelerable{/*implementation code for accelerable*/}
public class DieselEngine:Accelerable{/*implementation code for accelerable*/}
Monorailya que no son realmente dos palabras, es una sola palabra (compuesta). Un MonoRail sería una subclase de Rail en lugar de un tipo diferente de rail (que es). Al igual que no SunShineCupCakeSunshineCupcake
Si bien la mayoría de los patrones de diseño tienen nombres útiles, considero que el nombre "Puente" no es intuitivo con respecto a lo que hace.
Conceptualmente, inserta los detalles de implementación utilizados por una jerarquía de clases en otro objeto, generalmente con su propia jerarquía. Al hacerlo, está eliminando una dependencia estrecha de esos detalles de implementación y permitiendo que los detalles de esa implementación cambien.
En pequeña escala, comparo esto con el uso de un patrón de estrategia en la forma en que puede conectar un nuevo comportamiento. Pero en lugar de simplemente envolver un algoritmo como se ve a menudo en una estrategia, el objeto de implementación generalmente está más lleno de funciones. Y cuando aplica el concepto a toda una jerarquía de clases, el patrón más grande se convierte en un Puente. (De nuevo, odio el nombre).
No es un patrón que usará todos los días, pero lo he encontrado útil al manejar una explosión potencial de clases que puede ocurrir cuando tiene una (aparente) necesidad de herencia múltiple.
Aquí hay un ejemplo del mundo real:
Tengo una herramienta RAD que le permite colocar y configurar controles en una superficie de diseño, por lo que tengo un modelo de objetos como este:
Widget // base class with design surface plumbing
+ Top
+ Left
+ Width
+ Height
+ Name
+ SendToBack
+ BringToFront
+ OnPropertyEdit
+ OnSelect
+ Validate
+ ShowEditor
+ Paint
+ Etc
TextboxWidget : Widget // text box specific
+ Text
+ MaxLength
+ Font
+ ShowEditor // override base to show a property editor form specific to a Textbox
+ Paint // override to render a textbox onto the surface
+ Etc
ListWidget : Widget // list specific
+ Items
+ SelectedItem
+ ShowEditor // override base to show a property editor form specific to a List
+ Paint // override to render a list onto the surface
+ Etc
Y así sucesivamente, con quizás una docena de controles.
Pero luego se agrega un nuevo requisito para admitir múltiples temas (look-n-feel). Digamos que tenemos los siguientes temas: Win32, WinCE, WinPPC, WinMo50, WinMo65. Cada tema tendría diferentes valores o implementaciones para operaciones relacionadas con la representación como DefaultFont, DefaultBackColor, BorderWidth, DrawFrame, DrawScrollThumb, etc.
Podría crear un modelo de objeto como este:
Win32TextboxWidget : TextboxWidget
Win32ListWidget : ListWidget
etc., para un tipo de control
WinCETextboxWidget : TextboxWidget
WinCEListWidget : ListWidget
etc., para cada tipo de control (de nuevo)
Se entiende la idea: se obtiene una explosión de clase del número de widgets por el número de temas. Esto complica al diseñador de RAD haciéndolo consciente de todos y cada uno de los temas. Además, agregar nuevos temas obliga a modificar el diseñador RAD. Además, hay mucha implementación común dentro de un tema que sería genial heredar, pero los controles ya están heredando de una base común ( Widget).
Entonces, en cambio, lo que hice fue crear una jerarquía de objetos separada que implementa el tema. Cada widget contendría una referencia al objeto que implementa las operaciones de representación. En muchos textos, esta clase tiene el sufijo de una Implpero me desvié de esa convención de nomenclatura.
Así que ahora mi TextboxWidgetaspecto es el siguiente:
TextboxWidget : Widget // text box specific
+ Text
+ MaxLength
+ Font
+ ShowEditor
+ Painter // reference to the implementation of the widget rendering operations
+ Etc
Y puedo hacer que mis diversos pintores hereden mi base de temas específicos, lo que no podía hacer antes:
Win32WidgetPainter
+ DefaultFont
+ DefaultFontSize
+ DefaultColors
+ DrawFrame
+ Etc
Win32TextboxPainter : Win32WidgetPainter
Win32ListPainter : Win32WidgetPainter
Una de las cosas buenas es que puedo cargar dinámicamente las implementaciones en tiempo de ejecución, lo que me permite agregar tantos temas como quiera sin cambiar el software principal. En otras palabras, mi "implementación puede variar independientemente de la abstracción".
Win32TextboxPaintery Win32ListPainter de dónde provenía Win32WidgetPainter. Usted puede tener un árbol de herencia en el lado de la aplicación, pero debe ser más genérico (tal vez StaticStyleControlPainter, EditStyleControlPaintery ButtonStyleControlPainter) con las operaciones primitivas necesarias anulado, según sea necesario. Esto está más cerca del código real en el que estaba basando el ejemplo.
El puente tiene la intención de desacoplar una abstracción de su implementación concreta , de modo que ambos puedan variar independientemente:
El puente logra esto usando la composición:
Observaciones adicionales sobre una confusión frecuente
Este patrón es muy similar al patrón adaptador: la abstracción ofrece una interfaz diferente para una implementación y utiliza la composición para hacerlo. Pero:
La diferencia clave entre estos patrones radica en sus intenciones
: Gamma y otros, en " Patrones de diseño, elemento del software OO reutilizable " , 1995
En este excelente libro seminal sobre patrones de diseño, los autores también observan que: