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 ambas puedan variar independientemente" , introducido por la Banda de los Cuatro . [1] El puente utiliza 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 se pueden realizar cambios en el código de un programa 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. La clase en sí puede considerarse como la abstracción y lo que la clase puede hacer como la implementación . El patrón puente también puede considerarse 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 puente a menudo se confunde con el patrón adaptador , y a menudo se implementa utilizando el patrón adaptador de objetos ; por ejemplo, en el código Java a continuación.
Variante: La implementación se puede disociar aún más al posponer 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 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]
Al utilizar subclases, las distintas subclases implementan una clase abstracta de distintas maneras. Sin embargo, una implementación está vinculada a la abstracción en tiempo de compilación y no se puede modificar en tiempo de ejecución.
¿Qué solución describe el patrón de diseño 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 Abstraction
un Implementor
objeto en tiempo de ejecución.
Consulte también el diagrama de secuencia y clase de lenguaje de modelado unificado 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 su lugar, 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 (al delegar 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 (al llamar a ), que realiza la operación y regresa a .Abstraction1
Implementor1
operationImp()
Implementor1
Abstraction1
El patrón Bridge compone objetos en una 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 arquitectura verdaderamente desacoplada interfaz pública IBridge { void Function1 (); void Function2 (); } clase pública Bridge1 : IBridge { void público Function1 () { Console . WriteLine ( "Bridge1.Function1" ); } public void Función2 () { Console.WriteLine ( " Bridge1.Función2 " ); } } clase pública Bridge2 : IBridge { void público Función1 () { Console.WriteLine ( " Bridge2.Función1 " ); } public void Función2 () { Console.WriteLine ( " Bridge2.Función2 " ); } } interfaz pública IAbstractBridge { void CallMethod1 (); void CallMethod2 (); } clase pública AbstractBridge : IAbstractBridge { puente público IBridge ; público AbstractBridge ( IBridge puente ) { este . puente = puente ; } public void CallMethod1 ( ) { este.puente.Función1 ( ) ; } public void CallMethod2 ( ) { este.puente.Función2 ( ) ; } }
Las clases Bridge son la Implementación que utiliza 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 desacopladas entre sí.
clase abstracta DrawingAPI def abstracta draw_circle ( x : Float64 , y : Float64 , radio : Float64 ) fin clase DrawingAPI1 < DrawingAPI def draw_circle ( x : Flotante , y : Flotante , radio : Flotante ) "API1.circle en #{ x } : #{ y } - radio: #{ radio } " fin fin clase DrawingAPI2 < DrawingAPI def draw_circle ( x : Float64 , y : Float64 , radio : Float64 ) "API2.circle en #{ x } : #{ y } - radio: #{ radio } " fin fin Clase abstracta Forma protegida getter drawing_api : DrawingAPI def inicializar ( @drawing_api ) fin definición abstracta dibujar definición abstracta redimensionar_por_porcentaje ( porcentaje : Float64 ) fin clase CircleShape < Shape captador x : Float64 captador y : Float64 captador radio : Float64 def inicializar ( @x , @y , @radius , drawing_api : DrawingAPI ) super ( drawing_api ) fin def draw @drawing_api . draw_circle ( @x , @y , @radius ) fin def redimensionar_por_porcentaje ( porcentaje : Float64 ) @radius *= ( 1 + porcentaje / 100 ) fin fin clase BridgePattern def self . test formas = [] of Forma formas << CircleShape . new ( 1.0 , 2.0 , 3.0 , DrawingAPI1 . new ) formas << CircleShape . new ( 5.0 , 7.0 , 11.0 , DrawingAPI2 . new ) formas . cada uno hace | forma | forma . redimensionar_por_porcentaje ( 2.5 ) pone forma . dibujar fin fin fin Prueba BridgePattern
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 :: string DrawCircle ( float x , float y , float radio ) const = 0 ; }; clase DrawingAPI01 : público DrawingAPI { público : std :: string DrawCircle ( float x , float y , float radio ) const anulación { devolver "API01.circle en " + std :: to_string ( x ) + : " + std :: to_string ( y ) + " - radio: " + std :: to_string ( radio ); } }; clase DrawingAPI02 : público DrawingAPI { público : std :: string DrawCircle ( float x , float y , float radio ) const anulación { devolver "API02.circle en " + std :: to_string ( x ) + : " + std :: to_string ( y ) + " - radio: " + std :: to_string ( radio ); } }; clase Forma { público : Forma ( const DrawingAPI & drawing_api ) : drawing_api_ ( drawing_api ) {} virtual ~ Forma () = predeterminado ; virtual std :: string Draw () const = 0 ; virtual float ResizeByPercentage ( const float porcentaje ) = 0 ; protegido : const DrawingAPI & drawing_api_ ; }; clase CircleShape : public Shape { public : CircleShape ( float x , float y , float radio , const DrawingAPI & drawing_api ) : Forma ( drawing_api ), x_ ( x ), y_ ( y ), radio_ ( radio ) {} std :: string Draw ( ) const anular { devolver drawing_api_.DrawCircle ( x_ , y_ , radio_ ) ; } float ResizeByPercentage ( const float percent ) anular { devolver radio_ *= ( 1.0f + porcentaje / 100.0f ); } privado : float x_ , y_ , radio_ ; }; int main ( int argc , char ** argv ) { const DrawingAPI01 api1 {}; const DrawingAPI02 api2 {}; std :: vector < FormaCírculo > formas { FormaCírculo { 1.0f , 2.0f , 3.0f , api1 }, FormaCírculo { 5.0f , 7.0f , 11.0f , api2 } }; para ( auto & forma : formas ) { forma . ResizeByPercentage ( 2.5 ); std :: cout << forma . Draw () << std :: endl ; } devuelve 0 ; }
Producción:
API01.círculo en 1.000000:2.000000 - radio: 3.075000API02.círculo 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 interface Logger { void log ( String message ) ; static Logger info () { return message - > System.out.println ( " info: " + message ); } static Logger advertencia ( ) { return message - > System.out.println ( " advertencia: " + message ) ; } } clase abstracta AbstractAccount { private Logger logger = Logger.info (); public void setLogger ( Logger logger ) { this.logger = logger ; } // la parte de registro se delega a la implementación de Logger protected void operate ( String message , boolean result ) { logger.log ( message + " result " + result ) ; } } clase SimpleAccount extiende AbstractAccount { int privado saldo ; público SimpleAccount ( int saldo ) { this.saldo = saldo ; } booleano público isBalanceLow ( ) { devuelve saldo < 50 ; } vacío público retirar ( int monto ) { booleano shouldPerform = saldo >= monto ; if ( shouldPerform ) { saldo -= monto ; } operar ( "retirar" + monto , shouldPerform ) ; } } public class BridgeDemo { public static void main ( String [] args ) { SimpleAccount account = new SimpleAccount ( 100 ); account.return ( 75 ); if ( count.isBalanceLow ( ) ) { // también puede cambiar la implementación de Logger en tiempo de ejecución account.setLogger ( Logger.advertencia ( ) ) ; } account.return ( 10 ) ; account.return ( 100 ) ; } }
El resultado será:
info: retirar 75 resultado verdaderoAdvertencia: retirar 10 resultados verdaderosAdvertencia: retirar 100 resultado falso
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 " ; } } 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 ; función abstracta pública draw (); función abstracta pública resizeByPercentage ( $pct ); función protegida __construct ( DrawingAPI $drawingAPI ) { $this -> drawingAPI = $drawingAPI ; } }clase CircleShape extiende Shape { privado $x ; privado $y ; privado $radio ; función pública __construct ( $x , $y , $radio , DrawingAPI $drawingAPI ) { padre :: __construct ( $drawingAPI ); $this -> x = $x ; $this -> y = $y ; $this -> radio = $radio ; } función pública draw () { $this -> drawingAPI -> drawCircle ( $this -> x , $this -> y , $this -> radio ); } función pública resizeByPercentage ( $pct ) { $this -> radio *= $pct ; } }clase Tester { función pública estática principal () { $formas = matriz ( nueva CircleShape ( 1 , 3 , 7 , nueva DrawingAPI1 ()), nueva CircleShape ( 5 , 7 , 11 , nueva DrawingAPI2 ()), ); foreach ( $formas como $forma ) { $forma -> redimensionarPorPorcentaje ( 2.5 ); $forma -> dibujar (); } } }Probador :: principal ();
Producción:
API1.círculo con radio 1:3 17,5API2.círculo con radio 5:7 27,5
rasgo DrawingAPI { def drawCircle ( x : Doble , y : Doble , radio : Doble ) } clase DrawingAPI1 extiende DrawingAPI { def drawCircle ( x : Double , y : Double , radio : Double ) = println ( s"API #1 $ x $ y $ radio " ) } clase DrawingAPI2 extiende DrawingAPI { def drawCircle ( x : Double , y : Double , radio : Double ) = println ( s"API #2 $ x $ y $ radio " ) } clase abstracta Forma ( drawingAPI : DrawingAPI ) { def draw () def resizePercentage ( pct : Double ) } clase CircleShape ( x : Double , y : Double , var radio : Double , API de dibujo : API de dibujo ) extiende Shape ( API de dibujo : API de dibujo ) { def draw ( ) = drawingAPI.drawCircle ( x , y , radio ) def resizePercentage ( pct : Double ) { radio *= pct } } objeto 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 . draw () } } }
""" Ejemplo de patrón de puente. """ from abc import ABCMeta , abstractmethodNOT_IMPLEMENTED = "Deberías implementar esto."clase DrawingAPI : __metaclass__ = ABCMeta @abstractmethod def draw_circle ( self , x , y , radio ): genera NotImplementedError ( NO_IMPLEMENTADO )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 } "clase DrawingAPI3 ( DrawingAPI ): def draw_circle ( self , x , y , radio ): return f "API3.circle en { x } : { y } - radio: { radio } "Clase Forma : __metaclass__ = ABCMeta dibujo_api = Ninguno def __init__ ( self , dibujo_api ): self . dibujo_api = dibujo_api @abstractmethod def draw ( self ): genera NotImplementedError ( NO_IMPLEMENTADO ) @abstractmethod def redimensionar_por_porcentaje ( self , porcentaje ): generar NotImplementedError ( NO_IMPLEMENTADO )clase FormaCírculo ( Forma ) : def __init __ ( self , x , y , radio , api_dibujo ) : self.x = x self.y = y self.radio = radio super ( FormaCírculo , self ) .__ init __ ( api_dibujo ) def draw ( self ) : devuelve self.drawing_api.draw_circle ( self.x , self.y , self.radio ) def resize_by_percentage ( self , percent ) : self.radio * = 1 + percent / 100 clase BridgePattern : @staticmethod def prueba (): 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 forma en formas : forma.resize_by_percentage ( 2.5 ) print ( forma.draw ( ) ) Patrón de puente .test ()
{{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.