El patrón de puente es un patrón de diseño utilizado en ingeniería de software que pretende "desacoplar una abstracción de su implementación para que los dos puedan variar de forma independiente" , introducido por la Banda de los Cuatro . [1] El puente usa encapsulación , agregación y puede usar herencia para separar responsabilidades en diferentes clases .
Cuando una clase varía con frecuencia, las características de la programación orientada a objetos se vuelven muy útiles porque los cambios en el código de un programa se pueden hacer fácilmente con un conocimiento previo mínimo sobre el programa. El patrón de puente es útil cuando tanto la clase como lo que hace varían con frecuencia. La clase en sí puede considerarse como la abstracción y lo que la clase puede hacer como la implementación . El patrón del puente también se puede considerar como dos capas de abstracción.
Cuando solo hay una implementación fija, este patrón se conoce como el idioma Pimpl en el mundo de C ++ .
El patrón de puente a menudo se confunde con el patrón de adaptador y, a menudo, se implementa utilizando el patrón de adaptador de objeto ; por ejemplo, en el código Java a continuación.
Variante: la implementación se puede desacoplar aún más si se pospone la presencia de la implementación hasta el punto en que se utiliza la abstracción.
Descripción general
El patrón de diseño Bridge [2] es uno de los veintitrés patrones de diseño GoF bien conocidos que describen cómo resolver problemas de diseño recurrentes para 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 el patrón de diseño del puente? [3]
- Una abstracción y su implementación deben definirse y ampliarse independientemente una de la otra.
- Debe evitarse un enlace en tiempo de compilación entre una abstracción y su implementación para que se pueda seleccionar una implementación en tiempo de ejecución.
Al usar subclases, diferentes subclases implementan una clase abstracta de diferentes maneras. Pero una implementación está vinculada a la abstracción en tiempo de compilación y no se puede cambiar en tiempo de ejecución.
¿Qué solución describe el patrón de diseño del puente?
- Separe una abstracción (
Abstraction
) de su implementación (Implementor
) colocándolas en jerarquías de clases separadas. - Implemente el
Abstraction
en términos de (delegando a) unImplementor
objeto.
Esto permite configurar una Abstraction
con un Implementor
objeto en tiempo de ejecución.
Consulte también el diagrama de secuencia y la clase de Lenguaje de modelado unificado a continuación.
Estructura
Diagrama de secuencia y clase UML
En el diagrama de clases de Lenguaje de modelado unificado anterior , una abstracción ( Abstraction
) no se implementa como de costumbre en una jerarquía de herencia única. En cambio, hay una jerarquía para una abstracción ( Abstraction
) y una jerarquía separada para su implementación ( Implementor
), lo que hace que las dos sean independientes entre sí. La Abstraction
interfaz ( operation()
) se implementa en términos de (delegando a) la Implementor
interfaz ( imp.operationImp()
).
El diagrama de secuencia UML muestra las interacciones en tiempo de ejecución: el Abstraction1
objeto delega la implementación al Implementor1
objeto (llamando operationImp()
a Implementor1
), que realiza la operación y regresa a Abstraction1
.
Diagrama de clase
- Abstracción (clase abstracta)
- define la interfaz abstracta
- mantiene la referencia del Implementador.
- RefinedAbstraction (clase normal)
- extiende la interfaz definida por Abstraction
- Implementador (interfaz)
- define la interfaz para las clases de implementación
- ConcreteImplementor (clase normal)
- implementa la interfaz Implementor
Ejemplo
C#
El patrón de puente compone objetos en estructura de árbol. Desacopla la abstracción de la implementación. Aquí la abstracción representa el cliente desde el que se llamarán los objetos. A continuación se muestra un ejemplo implementado en C #
// Ayuda a proporcionar una interfaz pública de arquitectura verdaderamente desacoplada IBridge { void Function1 (); void Function2 (); } clase pública Bridge1 : IBridge { public void Function1 () { Console . WriteLine ( "Bridge1.Function1" ); } public void Function2 () { Console . WriteLine ( "Bridge1.Function2" ); } } clase pública Bridge2 : IBridge { public void Function1 () { Console . WriteLine ( "Puente2.Función1" ); } public void Function2 () { Console . WriteLine ( "Bridge2.Function2" ); } }pública interfaz IAbstractBridge { nula CallMethod1 (); void CallMethod2 (); } AbstractBridge de clase pública : IAbstractBridge { puente público de IBridge ; public AbstractBridge ( puente IBridge ) { this . puente = puente ; } public void CallMethod1 () { esto . tender un puente . Función1 (); } public void CallMethod2 () { esto . tender un puente . Función2 (); } }
Las clases Bridge son la implementación que usa la misma arquitectura orientada a la interfaz para crear objetos. Por otro lado, la abstracción toma una instancia de la clase de implementación y ejecuta su método. Por lo tanto, están completamente desacoplados entre sí.
Cristal
clase abstracta DrawingAPI abstract def draw_circle ( x : Float64 , y : Float64 , radio : Float64 ) endclase DrawingAPI1 < DrawingAPI def draw_circle ( x : Float , y : Float , radius : Float ) "API1.circle at # { x } : # { y } - radius: # { radius } " end endclase DrawingAPI2 < DrawingAPI def draw_circle ( x : Float64 , y : Float64 , radius : Float64 ) "API2.circle at # { x } : # { y } - radius: # { radius } " end end clase abstracta Forma protegida getter drawing_api : DrawingAPI def inicializar ( @drawing_api ) fin abstracta def dibujar abstracto def resize_by_percentage ( ciento : float64 ) finalclase CircleShape < Forma getter x : float64 getter y : float64 getter radio : float64 def initialize ( @x , @y , @radius , drawing_api : DrawingAPI ) super ( drawing_api ) end def dibuja @drawing_api . draw_circle ( @x , @y , @radius ) fin def resize_by_percentage ( percent : Float64 ) @radius * = ( 1 + percent / 100 ) end endclass BridgePattern def self . formas de prueba = [] de formas Shape << CircleShape . nuevo ( 1 . 0 , 2 . 0 , 3 . 0 , DrawingAPI1 . nuevas ) formas << CircleShape . nuevo ( 5 . 0 , 7 . 0 , 11 . 0 , DrawingAPI2 . nuevo ) formas . cada uno hace | forma | forma . resize_by_percentage ( 2 . 5 ) pone forma . dibujar extremo extremo extremoBridgePattern . prueba
Producción
API1.circle en 1.0: 2.0 - radio: 3.075API2.circle en 5.0: 7.0 - radio: 11.275
C ++
#include #include #include clase DrawingAPI { public : virtual ~ DrawingAPI () = predeterminado ; virtual std :: string DrawCircle ( flotante x , flotante y , radio flotante ) const = 0 ; }; class DrawingAPI01 : public DrawingAPI { public : std :: string DrawCircle ( float x , float y , float radius ) const override { return "API01.circle at" + std :: to_string ( x ) + ":" + std :: to_string ( y ) + "- radio:" + std :: to_string ( radio ); } };class DrawingAPI02 : public DrawingAPI { public : std :: string DrawCircle ( float x , float y , float radius ) const override { return "API02.circle at" + std :: to_string ( x ) + ":" + std :: to_string ( y ) + "- radio:" + std :: to_string ( radio ); } };class Shape { public : Shape ( const DrawingAPI & drawing_api ) : drawing_api_ ( drawing_api ) {} virtual ~ Shape () = predeterminado ; virtual std :: string Draw () const = 0 ; virtual float ResizeByPercentage ( const float percent ) = 0 ; protegido : const DrawingAPI & drawing_api_ ; };class CircleShape : public Shape { public : CircleShape ( float x , float y , float radius , const DrawingAPI y drawing_api ) : Shape ( drawing_api ), x_ ( x ), y_ ( y ), radius_ ( radius ) {} std :: string Draw () const override { return drawing_api_ . DrawCircle ( x_ , y_ , radio_ ); } float ResizeByPercentage ( const float percent ) override { return radius_ * = ( 1.0f + percent / 100.0f ); } privado : flotante x_ , y_ , radio_ ; };int main ( int argc , char ** argv ) { std :: vector < CircleShape > formas { CircleShape { 1.0f , 2.0f , 3.0f , DrawingAPI01 {}}, CircleShape { 5.0f , 7.0f , 11.0f , DrawingAPI02 { }} }; para ( auto y forma : formas ) { forma . ResizeByPercentage ( 2.5 ); std :: cout << forma . Draw () << std :: endl ; } return 0 ; }
Producción:
API01.circle en 1.000000: 2.000000 - radio: 3.075000API02.circle en 5.000000: 7.000000 - radio: 11.275000
Java
El siguiente programa Java define una cuenta bancaria que separa las operaciones de la cuenta del registro de estas operaciones.
// Logger tiene dos implementaciones: información y advertencia @FunctionalInterface interface Logger { void log ( mensaje de cadena ); static Logger info () { mensaje de retorno -> System . fuera . println ( "info:" + mensaje ); } advertencia de registrador estático () { mensaje de retorno -> Sistema . fuera . println ( "advertencia:" + mensaje ); } } abstracta clase AbstractAccount { privado Logger logger = Logger . info (); public void setLogger ( Logger logger ) { esto . logger = logger ; } // la parte de registro se delega a la implementación del registrador protected void operar ( mensaje de cadena , resultado booleano ) { logger . log ( mensaje + "resultado" + resultado ); } } class SimpleAccount extiende AbstractAccount { saldo int privado ; public SimpleAccount ( int balance ) { this . equilibrio = equilibrio ; } public boolean isBalanceLow () { devolver saldo < 50 ; } retiro público vacío ( cantidad int ) { boolean shouldPerform = balance > = amount ; if ( shouldPerform ) { saldo - = monto ; } operar ( "retirar" + monto , debe realizar ); } } public class BridgeDemo { public static void main ( String [] args ) { SimpleAccount account = new SimpleAccount ( 100 ); cuenta . retirarse ( 75 ); if ( account . isBalanceLow ()) { // también puede cambiar la implementación de Logger en la cuenta de tiempo de ejecución . setLogger ( Logger . advertencia ()); } cuenta . retirar ( 10 ); cuenta . retirar ( 100 ); } }
Saldrá:
información: retirar 75 resultado verdaderoadvertencia: retirar 10 resultado verdaderoadvertencia: retirar 100 resultado falso
PHP
interfaz DrawingAPI { función drawCircle ( $ x , $ y , $ radio ); }clase DrawingAPI1 implementa DrawingAPI { función pública drawCircle ( $ x , $ y , $ radius ) { echo "API1.circle en $ x : $ y radio $ radius . \ n " ; } } la clase DrawingAPI2 implementa DrawingAPI { función pública drawCircle ( $ x , $ y , $ radius ) { echo "API2.circle en $ x : $ y radio $ radius . \ n " ; } } Forma de clase abstracta { protected $ drawingAPI ; función pública abstracta dibujar (); función abstracta pública resizeByPercentage ( $ pct ); función protegida __construct ( DrawingAPI $ drawingAPI ) { $ this -> drawingAPI = $ drawingAPI ; } }clase CircleShape extiende Shape { privado $ x ; privado $ y ; radio $ privado ; función pública __construct ( $ x , $ y , $ radio , DrawingAPI $ drawingAPI ) { parent :: __construct ( $ drawingAPI ); $ esto -> x = $ x ; $ esto -> y = $ y ; $ esto -> radio = $ radio ; } función pública draw () { $ this -> drawingAPI -> drawCircle ( $ this -> x , $ this -> y , $ this -> radio ); } función pública resizeByPercentage ( $ pct ) { $ esto -> radio * = $ pct ; } }class Tester { función pública estática main () { $ formas = matriz ( nueva CircleShape ( 1 , 3 , 7 , nueva DrawingAPI1 ()), nueva CircleShape ( 5 , 7 , 11 , nueva DrawingAPI2 ()), ); foreach ( $ formas como $ forma ) { $ forma -> resizeByPercentage ( 2.5 ); $ forma -> dibujar (); } } }Tester :: main ();
Producción:
Círculo API1.en radio 1: 3 17,5Círculo API2 a 5: 7 de radio 27,5
Scala
Rasgo DrawingAPI { def drawCircle ( x : Double , y : Double , radio : Double ) }clase DrawingAPI1 extiende DrawingAPI { def drawCircle ( x : Double , y : Double , radius : Double ) = println ( s "API # 1 $ x $ y $ radius " ) } clase DrawingAPI2 extiende DrawingAPI { def drawCircle ( x : Double , y : Double , radius : Double ) = println ( s "API # 2 $ x $ y $ radius " ) } Forma de clase abstracta ( drawingAPI : DrawingAPI ) { def draw () def resizePercentage ( pct : Double ) } clase CircleShape ( x : Double , y : Double , var radio : Double , drawingAPI : DrawingAPI ) extiende Shape ( drawingAPI : DrawingAPI ) { def dibujar () = drawingAPI . drawCircle ( x , y , radio ) def resizePercentage ( pct : Double ) { radio * = pct } }object BridgePattern { def main ( args : Array [ String ]) { Seq ( new CircleShape ( 1 , 3 , 5 , new DrawingAPI1 ), new CircleShape ( 4 , 5 , 6 , new DrawingAPI2 ) ) foreach { x => x . resizePercentage ( 3 ) x . dibujar () } } }
Pitón
"" " Ejemplo de patrón de puente. " "" De abc import ABCMeta , método abstractoNOT_IMPLEMENTED = "Debería implementar esto".clase DrawingAPI : __metaclass__ = ABCMeta @abstractmethod def draw_circle ( self , x , y , radius ): levanta NotImplementedError ( NOT_IMPLEMENTED )clase DrawingAPI1 ( DrawingAPI ): def draw_circle ( self , x , y , radius ): return f "API1.circle at { x } : { y } - radius: { radius } "clase DrawingAPI2 ( DrawingAPI ): def draw_circle ( self , x , y , radius ): return f "API2.circle en { x } : { y } - radio: { radius } "clase DrawingAPI3 ( DrawingAPI ): def draw_circle ( self , x , y , radius ): return f "API3.circle en { x } : { y } - radio: { radius } " Forma de clase : __metaclass__ = ABCMeta drawing_api = Ninguno def __init__ ( self , drawing_api ): self . drawing_api = drawing_api @abstractmethod def draw ( self ): levanta NotImplementedError ( NOT_IMPLEMENTED ) @abstractmethod def resize_by_percentage ( self , percent ): aumentar NotImplementedError ( NOT_IMPLEMENTED )class CircleShape ( Shape ): def __init__ ( self , x , y , radius , drawing_api ): self . x = x uno mismo . y = y self . radio = radio super ( CircleShape , self ) . __init__ ( drawing_api ) def dibujar ( self ): return self . drawing_api . draw_circle ( self . x , self . y , self . radio ) def resize_by_percentage ( self , percent ): self . radio * = 1 + porcentaje / 100class BridgePattern : @staticmethod def test (): shapes = [ CircleShape ( 1.0 , 2.0 , 3.0 , DrawingAPI1 ()), CircleShape ( 5.0 , 7.0 , 11.0 , DrawingAPI2 ()), CircleShape ( 5.0 , 4.0 , 12.0 , DrawingAPI3 () ), ] para la forma en las formas : forma . resize_by_percentage ( 2.5 ) imprimir ( forma . dibujar ())BridgePattern . prueba ()
Ver también
Referencias
- ^ Gamma, E, Helm, R, Johnson, R, Vlissides, J: Design Patterns , página 151. Addison-Wesley, 1995
- ^ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Patrones de diseño: elementos de software orientado a objetos reutilizable . Addison Wesley. págs. 151ff . ISBN 0-201-63361-2.CS1 maint: varios nombres: lista de autores ( enlace )
- ^ "El patrón de diseño del puente: problema, solución y aplicabilidad" . w3sDesign.com . Consultado el 12 de agosto de 2017 .
- ^ "El patrón de diseño del puente - Estructura y colaboración" . w3sDesign.com . Consultado el 12 de agosto de 2017 .
enlaces externos
- Bridge en UML y en LePUS3 (un lenguaje de modelado formal)
- Patrones de diseño de C #: el patrón de puente . Capítulo de muestra . 2002-12-20. De: James W. Cooper (2003). Patrones de diseño de C #: un tutorial . Addison-Wesley . ISBN 0-201-84453-2.