El patrón 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 las 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 realizar fácilmente con un conocimiento previo mínimo sobre el programa. El patrón puente es útil cuando tanto la clase como lo que hace varían con frecuencia. Se puede considerar la clase misma 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 modismo Pimpl en el mundo C++ .
El patrón puente a menudo se confunde con el patrón adaptador y, a menudo, se implementa utilizando el patrón adaptador de objeto ; por ejemplo, en el código Java siguiente.
Variante: la implementación se puede desacoplar aún más difiriendo la presencia de la implementación hasta el punto en que se utiliza la abstracción.
El patrón de diseño Bridge 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. [1]
¿Qué problemas puede resolver el patrón de diseño Bridge? [2]
Cuando se utilizan 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?
Abstraction
) de su implementación ( Implementor
) colocándolas en jerarquías de clases separadas.Abstraction
en términos de (delegando a) un Implementor
objeto.Esto permite configurar un Abstraction
con un Implementor
objeto en tiempo de ejecución.
Consulte también la clase del Lenguaje de modelado unificado y el diagrama de secuencia a continuación.
En el diagrama de clases del Lenguaje de modelado unificado anterior , una abstracción ( Abstraction
) no se implementa como es habitual en una única jerarquía de herencia. En cambio, hay una jerarquía para una abstracción ( Abstraction
) y una jerarquía separada para su implementación ( Implementor
), lo que las hace 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 objeto delega la implementación al objeto (llamando a ), que realiza la operación y regresa a .Abstraction1
Implementor1
operationImp()
Implementor1
Abstraction1
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 cual se llamarán los objetos. A continuación se proporciona un ejemplo implementado en C#.
// Ayuda a proporcionar una arquitectura verdaderamente desacoplada public interface IBridge { void Function1 (); Función vacía2 (); } clase pública Puente1 : IBridge { función pública vacía1 () { Consola . WriteLine ( "Puente1.Función1" ); } public void Function2 () { Consola . WriteLine ( "Puente1.Función2" ); } } clase pública Puente2 : IBridge { función pública vacía1 () { Consola . WriteLine ( "Puente2.Función1" ); } public void Function2 () { Consola . WriteLine ( "Bridge2.Function2" ); } } interfaz pública IAbstractBridge { void CallMethod1 (); void CallMethod2 (); } clase pública AbstractBridge : IAbstractBridge { puente público IBridge ; public AbstractBridge ( puente IBridge ) { este . puente = puente ; } public void CallMethod1 () { esto . puente . Función1 (); } public void CallMethod2 () { esto . puente . Función2 (); } }
Las clases Bridge son la implementación que utiliza la misma arquitectura orientada a 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 tanto, están completamente desacoplados unos de otros.
clase abstracta DrawingAPI abstract def draw_circle ( x : Float64 , y : Float64 , radio : Float64 ) fin clase DrawingAPI1 < DrawingAPI def draw_circle ( x : Float , y : Float , radio : Float ) "API1.circle en #{ x } : #{ y } - radio: #{ radio } " end end clase DrawingAPI2 < DrawingAPI def draw_circle ( x : Float64 , y : Float64 , radio : Float64 ) "API2.circle en #{ x } : #{ y } - radio: #{ radio } " end end clase abstracta Forma protegida getter draw_api : DrawingAPI def inicializar ( @drawing_api ) finalizar resumen def dibujar resumen def resize_by_percentage ( porcentaje : Float64 ) fin clase CircleShape < Captador de forma x : Captador Float64 y : Captador Float64 radio : Float64 def inicializar ( @x , @y , @radius , dibujo_api : DrawingAPI ) super ( dibujo_api ) fin def dibujar @drawing_api . draw_circle ( @x , @y , @radius ) fin def resize_by_percentage ( porcentaje : Float64 ) @radius *= ( 1 + porcentaje / 100 ) fin fin clase BridgePattern def self . formas de prueba = [] de formas de formas << CircleShape . nuevas ( 1.0 , 2.0 , 3.0 , DrawingAPI1 . new ) 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 final final final Patrón de puente . prueba
Producción
API1.circle en 1.0:2.0 - radio: 3.075API2.circle en 5.0:7.0 - radio: 11.275
#include <iostream> #include <cadena> #include <vector> clase DrawingAPI { público : virtual ~ DrawingAPI () = predeterminado ; virtual std :: cadena DrawCircle ( flotante x , flotante y , radio flotante ) const = 0 ; }; clase DrawingAPI01 : public DrawingAPI { public : std :: string DrawCircle ( float x , float y , float radio ) const override { return "API01.circle at " + std :: to_string ( x ) + ":" + std :: to_string ( y ) + " - radio: " + std :: to_string ( radio ); } }; clase DrawingAPI02 : public DrawingAPI { public : std :: string DrawCircle ( float x , float y , float radio ) const override { return "API02.circle at " + std :: to_string ( x ) + ":" + std :: to_string ( y ) + " - radio: " + std :: to_string ( radio ); } }; clase Forma { público : Forma ( const DrawingAPI & dibujo_api ) : dibujo_api_ ( dibujo_api ) {} virtual ~ Forma () = predeterminado ; virtual std :: cadena Draw () const = 0 ; flotante virtual ResizeByPercentage ( porcentaje flotante constante ) = 0 ; protegido : const DrawingAPI & dibujo_api_ ; }; clase CircleShape : forma pública { público : CircleShape ( flotante x , flotante y , radio flotante , constante API de dibujo y API_dibujo ) : forma ( api_dibujo ), x_ ( x ), y_ ( y ), radio_ ( radio ) {} std :: cadena Draw () const override { return dibujo_api_ . DibujarCírculo ( x_ , y_ , radio_ ); } float ResizeByPercentage ( porcentaje flotante constante ) anular { radio de retorno_ *= ( 1.0f + porcentaje / 100.0f ); } privado : flotante x_ , y_ , radio_ ; }; int main ( int argc , char ** argv ) { const DrawingAPI01 api1 {}; const DibujoAPI02 api2 {}; std :: vector < CircleShape > formas { CircleShape { 1.0f , 2.0f , 3.0f , api1 }, CircleShape { 5.0f , 7.0f , 11.0f , api2 } }; for ( auto & forma : formas ) { forma . Cambiar tamaño por porcentaje ( 2.5 ); std :: cout << forma . Dibujar () << std :: endl ; } devolver 0 ; }
Producción:
API01.circle en 1.000000:2.000000 - radio: 3.075000API02.circle en 5.000000:7.000000 - radio: 11.275000
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 interfaz Logger { void log ( string message ); Información del registrador estático () { mensaje de retorno -> Sistema . afuera . println ( "informacion: " + mensaje ); } Advertencia de registrador estático () { mensaje de retorno -> Sistema . afuera . println ( "advertencia: " + mensaje ); } } clase abstracta AbstractAccount { logger privado logger = Logger . información (); public void setLogger ( Logger registrador ) { this . registrador = registrador ; } // la parte de registro se delega a la implementación del registrador protected void operar ( mensaje de cadena , resultado booleano ) { logger . iniciar sesión ( mensaje + "resultado" + resultado ); } } clase CuentaSimple extiende CuentaAbstracta { saldo int privado ; cuenta simple pública ( saldo int ) { this . saldo = equilibrio ; } public boolean isBalanceLow () { saldo de retorno < 50 ; } retiro nulo público ( monto int ) { booleano deberíaPerformar = saldo >= monto ; if ( debería realizar ) { saldo -= monto ; } operar ( "retirar" + monto , debería realizar ); } } clase pública BridgeDemo { public static void main ( String [] args ) { Cuenta SimpleAccount = nueva Cuenta Simple ( 100 ); cuenta . retirarse ( 75 ); if ( cuenta . isBalanceLow ()) { // también puede cambiar la implementación del registrador en tiempo de ejecución de la cuenta . setLogger ( Logger . advertencia ()); } cuenta . retirar ( 10 ); cuenta . retirar ( 100 ); } }
Dará salida:
información: retirar 75 resultados verdaderosadvertencia: retirar 10 resultados verdaderosadvertencia: retirar 100 resultados falsos
interfaz DrawingAPI { función dibujarCircle ( $x , $y , $radius ); }clase DrawingAPI1 implementa DrawingAPI { función pública drawCircle ( $x , $y , $radius ) { echo "API1.circle en $x : $y radio $radius . \n " ; } } clase DrawingAPI2 implementa DrawingAPI { función pública drawCircle ( $x , $y , $radius ) { echo "API2.circle en $x : $y radio $radius . \n " ; } } clase abstracta Forma { protegida $drawingAPI ; sorteo de función abstracta pública (); función abstracta pública resizeByPercentage ( $pct ); función protegida __construct ( API de dibujo $API de dibujo ) { $this -> API de dibujo = $API de dibujo ; } }class CircleShape extiende Shape { privado $x ; privado $y ; radio $ privado ; función pública __construct ( $x , $y , $radius , DrawingAPI $drawingAPI ) { padre :: __construct ( $drawingAPI ); $esto -> x = $x ; $esto -> y = $y ; $esto -> radio = $radio ; } función pública dibujar () { $this -> dibujoAPI -> drawCircle ( $this -> x , $this -> y , $this -> radio ); } función pública resizeByPercentage ( $pct ) { $this -> radio *= $pct ; } }class Tester { public static function main () { $shapes = array ( nueva CircleShape ( 1 , 3 , 7 , nueva DrawingAPI1 ()), nueva CircleShape ( 5 , 7 , 11 , nueva DrawingAPI2 ()), ); foreach ( $formas como $forma ) { $forma -> resizeByPercentage ( 2.5 ); $forma -> dibujar (); } } }Probador :: principal ();
Producción:
API1.círculo con un radio de 1:3 17,5API2.círculo con un radio de 5:7 27,5
rasgo DrawingAPI { def drawCircle ( x : Doble , y : Doble , radio : Doble ) } clase DrawingAPI1 extiende DrawingAPI { def drawCircle ( x : Doble , y : Doble , radio : Doble ) = println ( s"API #1 $ x $ y $ radio " ) } clase DrawingAPI2 extiende DrawingAPI { def drawCircle ( x : Doble , y : Doble , radio : Doble ) = println ( s"API #2 $ x $ y $ radio " ) } clase abstracta Forma ( DrawingAPI : DrawingAPI ) { def draw () def resizePercentage ( pct : Double ) } clase CircleShape ( x : Doble , y : Doble , var radio : Doble , dibujoAPI : DibujoAPI ) extiende Forma ( dibujoAPI : DibujoAPI ) { def dibujar () = API de dibujo . dibujarCírculo ( x , y , radio ) def resizePercentage ( pct : Doble ) { radio *= pct } } object BridgePattern { def main ( args : Array [ String ] ) { Seq ( nueva CircleShape ( 1 , 3 , 5 , nueva DrawingAPI1 ), nueva CircleShape ( 4 , 5 , 6 , nueva DrawingAPI2 ) ) foreach { x => x . cambiar tamañoPorcentaje ( 3 ) x . dibujar () } } }
""" Ejemplo de patrón de puente. """ de abc import ABCMeta , método abstractoNOT_IMPLEMENTED = "Deberías implementar esto."clase DrawingAPI : __metaclass__ = ABCMeta @abstractmethod def draw_circle ( self , x , y , radio ): levanta NotImplementedError ( NOT_IMPLEMENTED )clase DrawingAPI1 ( DrawingAPI ): def draw_circle ( self , x , y , radio ): return f "API1.circle en { x } : { y } - radio: { radio } "clase DrawingAPI2 ( DrawingAPI ): def draw_circle ( self , x , y , radio ): return f "API2.circle en { x } : { y } - radio: { radio } "class DrawingAPI3 ( DrawingAPI ): def draw_circle ( self , x , y , radio ): return f "API3.circle en { x } : { y } - radio: { radio } " Forma de clase : __metaclass__ = ABCMeta dibujo_api = Ninguno def __init__ ( self , dibujo_api ): self . dibujo_api = dibujo_api @abstractmethod def dibujar ( self ): generar NotImplementedError ( NOT_IMPLEMENTED ) @abstractmethod def resize_by_percentage ( self , porcentaje ): elevar NotImplementedError ( NOT_IMPLEMENTED )clase CircleShape ( Forma ): def __init__ ( self , x , y , radio , dibujo_api ): self . x = x uno mismo . y = y yo . radio = radio super ( CircleShape , self ) . __init__ ( dibujo_api ) def dibujar ( self ): devolver self . dibujo_api . draw_circle ( self.x , self.y , self.radio ) def resize_by_percentage ( self , porcentaje ): self . radio *= 1 + por ciento / 100clase BridgePattern : @staticmethod def test (): formas = [ 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 ())Patrón de puente . prueba ()
{{cite book}}
: |work=
ignorado ( ayuda ) De: James W. Cooper (2003). Patrones de diseño de C#: un tutorial. Addison-Wesley . ISBN 0-201-84453-2.