El patrón puente es un patrón de diseño utilizado en ingeniería de software que tiene como objetivo "desacoplar una abstracción de su implementación para que ambas puedan variar independientemente" , introducido por el Grupo de los Cuatro . [ 1 ] El puente utiliza encapsulación , agregación y puede utilizar herencia para separar responsabilidades en diferentes clases .
Cuando una clase varía con frecuencia, las características de la programación orientada a objetos resultan muy útiles, ya que los cambios en el código del programa se pueden realizar fácilmente con un conocimiento previo mínimo del mismo. El patrón puente es útil cuando tanto la clase como su funcionalidad varían a menudo. La clase en sí puede considerarse la abstracción y su funcionalidad, la implementación . El patrón puente también puede entenderse como dos capas de abstracción.
Cuando solo existe una implementación fija, este patrón se conoce como el modismo 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 desacoplar aún más aplazando 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 es uno de los veintitrés patrones de diseño GoF más 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 ]
- Una abstracción y su implementación deben definirse y ampliarse de forma independiente entre sí.
- Debe evitarse la vinculación en tiempo de compilación entre una abstracción y su implementación, de modo que se pueda seleccionar una implementación en tiempo de ejecución.
Al utilizar la herencia, las distintas subclases implementan una clase abstracta de maneras diferentes. Sin embargo, la implementación queda vinculada a la abstracción en tiempo de compilación y no puede modificarse en tiempo de ejecución.
¿Qué solución describe el patrón de diseño Bridge?
- Separe una abstracción (
Abstraction) de su implementación (Implementor) colocándolas en jerarquías de clases separadas. - Implementar
Abstractionen términos de (delegando a) unImplementorobjeto.
Esto permite configurar un Abstractionobjeto Implementoren tiempo de ejecución. Consulte también el diagrama de clases y secuencia del Lenguaje Unificado de Modelado (UML) que aparece a continuación.
Estructura
Diagrama de clases y secuencia UML

En el diagrama de clases del Lenguaje Unificado de Modelado (UML) 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 hace que ambas sean independientes entre sí. La Abstractioninterfaz ( operation()) se implementa en términos de (delegando a) la Implementorinterfaz ( 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 .Abstraction1Implementor1operationImp()Implementor1Abstraction1
Diagrama de clases
![]()
- Abstracción (clase abstracta)
- define la interfaz abstracta
- Mantiene la referencia del implementador.
- RefinedAbstraction (clase normal)
- extiende la interfaz definida por Abstracción
- Implementador (interfaz)
- define la interfaz para las clases de implementación
- Implementador de hormigón (clase normal)
- implementa la interfaz Implementor

Ejemplo
DO#
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 cual se llamarán los objetos. A continuación se muestra un ejemplo implementado en C#.
espacio de nombres Wikipedia.Ejemplos ;usando el sistema ;// Ayuda a proporcionar una interfaz de arquitectura verdaderamente desacoplada IBridge { void Function1 (); void Function2 (); }clase Bridge1 : IBridge { public void Function1 () { Console . WriteLine ( "Bridge1.Function1" ); }public void Function2 () { Console . WriteLine ( "Bridge1.Function2" ); } }clase Bridge2 : IBridge { public void Function1 () { Console . WriteLine ( "Bridge2.Function1" ); }public void Function2 () { Console . WriteLine ( "Bridge2.Function2" ); } }interfaz IAbstractBridge { void CallMethod1 (); void CallMethod2 (); }clase AbstractBridge : IAbstractBridge { public IBridge bridge ;public AbstractBridge ( IBridge bridge ) { this . bridge = bridge ; }public void CallMethod1 () { this . bridge . Function1 (); }public void CallMethod2 () { this . bridge . Function2 (); } }Las clases Bridge son la implementación que utiliza la misma arquitectura orientada a interfaces para crear objetos. Por otro lado, la abstracción toma una instancia de la clase de implementación y ejecuta su método. De esta forma, están completamente desacopladas entre sí.
Cristal
clase abstracta DrawingAPI abstract def draw_circle ( x : Float64 , y : Float64 , radius : Float64 ) endclase DrawingAPI1 < DrawingAPI def dibujar_círculo ( x : Float , y : Float , radio : Float ) "API1.círculo en #{ x } : #{ y } - radio: #{ radio } " fin finclase DrawingAPI2 < DrawingAPI def dibujar_círculo ( x : Float64 , y : Float64 , radio : Float64 ) "API2.círculo en #{ x } : #{ y } - radio: #{ radio } " fin finclase abstracta Shape protegido getter drawing_api : DrawingAPIdef inicializar ( @drawing_api ) finabstracto def dibujar abstracto def redimensionar_por_porcentaje ( por ciento : Float64 ) finclase CircleShape < Shape getter x : Float64 getter y : Float64 getter radius : Float64def initialize ( @x , @y , @radius , drawing_api : DrawingAPI ) super ( drawing_api ) enddef dibujar @drawing_api . dibujar_círculo ( @x , @y , @radius ) findef redimensionar_por_porcentaje ( porcentaje : Float64 ) @radio *= ( 1 + porcentaje / 100 ) fin finclase BridgePattern def self . test shapes = [] of Shape shapes << CircleShape . new ( 1.0 , 2.0 , 3.0 , DrawingAPI1 . new ) shapes << CircleShape . new ( 5.0 , 7.0 , 11.0 , DrawingAPI2 . new )formas . cada hacer | forma | forma . redimensionar_por_porcentaje ( 2.5 ) poner forma . dibujar fin fin finBridgePattern.pruebaProducción
API1.circle en 1.0:2.0 - radio: 3.075 API2.circle en 5.0:7.0 - radio: 11.275
C++
importar std ;using std :: string ; using std :: vector ;clase DrawingAPI { public : virtual ~ DrawingAPI () = default ; virtual string drawCircle ( float x , float y , float radius ) const = 0 ; };clase DrawingAPI01 : public DrawingAPI { public : [[ nodiscard ]] string drawCircle ( float x , float y , float radius ) const override { return std :: format ( "API01.circle at {}:{} - radius: {}" , x , y , radius ); } };clase DrawingAPI02 : public DrawingAPI { public : [[ nodiscard ]] string drawCircle ( float x , float y , float radius ) const override { return std :: format ( "API02.circle at {}:{} - radius: {}" , x , y , radius ); } };clase Shape { protected : const DrawingAPI & drawingApi ; public : Shape ( const DrawingAPI & api ) : drawingApi { api } {}virtual ~ Forma () = predeterminado ;virtual string draw () const = 0 ; virtual float resizeByPercentage ( const float percent ) noexcept = 0 ; };clase CircleShape : public Shape { private : float x ; float y ; float radius ; public : CircleShape ( const DrawingAPI & api , float x , float y , float radius ) : Shape ( api ), x { x }, y { y }, radius { radius } {}[[ nodiscard ]] string draw () const override { return drawingApi . drawCircle ( x , y , radius ); }[[ nodiscard ]] float resizeByPercentage ( float percent ) noexcept override { return radius *= ( 1.0f + percent / 100.0f ); } };int main ( int argc , char * argv []) { const DrawingAPI01 api1 ; const DrawingAPI02 api2 ; vector < CircleShape > shapes { CircleShape { api1 , 1.0f , 2.0f , 3.0f }, CircleShape { api2 , 5.0f , 7.0f , 11.0f } };for ( CircleShape & shape : shapes ) { shape . resizeByPercentage ( 2.5 ); std :: println ( "{}" , shape . draw ()); }devolver 0 ; }Producción:
API01.circle en 1.000000:2.000000 - radio: 3.075000 API02.circle en 5.000000:7.000000 - radio: 11.275000
El código requiere un compilador compatible con C++23; por ejemplo, en Ubuntu 26.06 LTS con g++-15 se requiere la siguiente compilación en 2 pasos:
g++-15 -std=c++23 -c -fmodules -fmodule-only -fsearch-include-path bits/std.cc g++-15 -std=c++23 -fmodules -o programa program.cpp
Java
El siguiente programa Java define una cuenta bancaria que separa las operaciones de la cuenta del registro de dichas operaciones.
paquete org.wikipedia.examples ;// El registrador tiene dos implementaciones: info y warning @FunctionalInterface interface Logger { void log ( String message ); static Logger info () { return message -> System . out . printf ( "info: %s%n" , message ); } static Logger warning () { return message -> System . out . printf ( "warning: %s%n" , 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 ( String.format ( " % s resultado %s" , message , result ) ) ; } }clase SimpleAccount extiende AbstractAccount { private int balance ; public SimpleAccount ( int balance ) { this . balance = balance ; } public boolean isBalanceLow () { return balance < 50 ; } public void withdraw ( int amount ) { boolean shouldPerform = balance >= amount ; if ( shouldPerform ) { balance -= amount ; } operate ( String . format ( "withdraw %s" , amount , shouldPerform )); } }public class BridgeDemo { public static void main ( String [] args ) { SimpleAccount account = new SimpleAccount ( 100 ); account . withdraw ( 75 ); if ( account . isBalanceLow ()) { // También puedes cambiar la implementación de Logger en tiempo de ejecución account . setLogger ( Logger . warning ()); } account . withdraw ( 10 ); account . withdraw ( 100 ); } }El resultado será:
Información: resultado de retiro 75 verdadero Advertencia: resultado de retiro 10 verdadero Advertencia: el resultado de retirar 100 es falso.
PHP
interface DrawingAPI { function drawCircle ( $x , $y , $radius ); }class DrawingAPI1 implements DrawingAPI { public function drawCircle ( $x , $y , $radius ) { echo "API1.circle at $x : $y radius $radius . \n " ; } }class DrawingAPI2 implements DrawingAPI { public function drawCircle ( $x , $y , $radius ) { echo "API2.circle at $x : $y radius $radius . \n " ; } }clase abstracta Shape { protected $drawingAPI ;función abstracta pública dibujar (); función abstracta pública redimensionarPorPorcentaje ( $pct );protected function __construct ( DrawingAPI $drawingAPI ) { $this -> drawingAPI = $drawingAPI ; } }clase CircleShape extiende Shape { private $x ; private $y ; private $radius ;public function __construct ( $x , $y , $radius , DrawingAPI $drawingAPI ) { parent :: __construct ( $drawingAPI ); $this -> x = $x ; $this -> y = $y ; $this -> radius = $radius ; }public function draw () { $this -> drawingAPI -> drawCircle ( $this -> x , $this -> y , $this -> radius ); }función pública redimensionarPorPorcentaje ( $pct ) { $this -> radio *= $pct ; } }classTester{publicstaticfunctionmain(){$shapes=array(newCircleShape(1,3,7,newDrawingAPI1()),newCircleShape(5,7,11,newDrawingAPI2()),);foreach($shapesas$shape){$shape->resizeByPercentage(2.5);$shape->draw();}}}Tester::main();Output:
API1.circle at 1:3 radius 17.5 API2.circle at 5:7 radius 27.5
Scala
traitDrawingAPI{defdrawCircle(x:Double,y:Double,radius:Double)}classDrawingAPI1extendsDrawingAPI{defdrawCircle(x:Double,y:Double,radius:Double)=println(s"API #1 $x$y$radius")}classDrawingAPI2extendsDrawingAPI{defdrawCircle(x:Double,y:Double,radius:Double)=println(s"API #2 $x$y$radius")}abstractclassShape(drawingAPI:DrawingAPI){defdraw()defresizePercentage(pct:Double)}clase CircleShape ( x : Double , y : Double , var radius : Double , drawingAPI : DrawingAPI ) extiende Shape ( drawingAPI : DrawingAPI ) {def dibujar ( ) = drawingAPI.drawCircle ( x , y , radio )def redimensionarPorcentaje ( 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 () } } }Pitón
""" Ejemplo de patrón puente. """ from abc import ABCMeta , abstractmethod from typing import NoReturnNO_IMPLEMENTADO : str = "Deberías implementar esto."clase DrawingAPI : __metaclass__ = ABCMeta@abstractmethod def draw_circle ( self , x : float , y : float , radius : float ) -> NoReturn : raise NotImplementedError ( NOT_IMPLEMENTED )clase DrawingAPI1 ( DrawingAPI ): def draw_circle ( self , x : float , y : float , radius : float ) -> str : return f "API1.circle en { x } : { y } - radio: { radius } "clase DrawingAPI2 ( DrawingAPI ): def draw_circle ( self , x : float , y : float , radius : float ) -> str : return f "API2.circle en { x } : { y } - radio: { radius } "clase DrawingAPI3 ( DrawingAPI ): def draw_circle ( self , x : float , y : float , radius : float ) -> str : return f "API3.circle en { x } : { y } - radio: { radius } "Clase Forma : __metaclase__ = ABCMetadrawing_api : DrawingAPI = None def __init __ ( self , drawing_api : DrawingAPI ) - > None : self.drawing_api = drawing_api@abstractmethod def draw ( self ) -> NoReturn : raise NotImplementedError ( NOT_IMPLEMENTED )@abstractmethod def resize_by_percentage ( self , percent : float ) -> NoReturn : raise NotImplementedError ( NOT_IMPLEMENTED )clase CircleShape ( Forma ): def __init__ ( self , x : float , y : float , radius : float , drawing_api : DrawingAPI ): self . x = x self . y = y self . radius = radius super ( CircleShape , self ) . __init__ ( drawing_api )def draw ( self ) -> str : return self . drawing_api . draw_circle ( self . x , self . y , self . radius )def redimensionar_por_porcentaje ( self , porcentaje : float ) -> None : self . radio *= 1 + porcentaje / 100clase BridgePattern : @staticmethod def test () -> None : shapes : list [ CircleShape ] = [ 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.redimensionar_por_porcentaje ( 2.5 ) imprimir ( forma.dibujar ( ) )if __name__ == "__main__" : BridgePattern . test ()Véase también
Referencias
- 1 2 Gamma, Erich ; Helm, Richard; Johnson, Ralph ; Vlissides, John (1994). Patrones de diseño: Elementos de software orientado a objetos reutilizable . Addison-Wesley. pág . 151. ISBN 0-201-63361-2.
- ↑ "El patrón de diseño Bridge: problema, solución y aplicabilidad" . w3sDesign.com . Consultado el 12 de agosto de 2017 .
- ↑ "El patrón de diseño Bridge: estructura y colaboración" . w3sDesign.com . Consultado el 12 de agosto de 2017 .
Enlaces externos
- Integración en UML y en LePUS3 (un lenguaje de modelado formal).
- Patrones de diseño en C#: El patrón puente. De: James W. Cooper (2003). Patrones de diseño en C#: Un tutorial . Addison-Wesley . ISBN 0-201-84453-2.
- patrones de diseño de software