Patrón de puente

Patrón de diseño utilizado en ingeniería de software

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.

Descripción general

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]

  • Una abstracción y su implementación deben definirse y extenderse independientemente una de otra.
  • Se debe evitar una vinculación 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 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?

  • 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) un Implementorobjeto.

Esto permite configurar Abstractionun Implementorobjeto en tiempo de ejecución.
Consulte también el diagrama de secuencia y clase de lenguaje de modelado unificado a continuación.

Estructura

Diagrama de clases y secuencias UML

Un ejemplo de diagrama de clase y secuencia UML para el patrón de diseño Bridge. [3]

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 Abstractioninterfaz ( operation()) se implementa en términos de (al delegar 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 (al llamar 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.
Abstracción refinada (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
Puente en LePUS3 (leyenda)

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 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í.

Cristal

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

C++

#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

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 ( 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

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 " ; } }       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

Escala

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 () } } }                           

Pitón

""" 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 ()

Véase también

Referencias

  1. ^ ab 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.
  2. ^ "El patrón de diseño Bridge: problema, solución y aplicabilidad". w3sDesign.com . Consultado el 12 de agosto de 2017 .
  3. ^ "El patrón de diseño Bridge - Estructura y colaboración". w3sDesign.com . Consultado el 12 de agosto de 2017 .
  • Puente en UML y en LePUS3 (un lenguaje de modelado formal)
  • Patrones de diseño de C#: El patrón Puente. 20-12-2002. {{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.
Obtenido de "https://es.wikipedia.org/w/index.php?title=Patrón_de_puente&oldid=1183383676"