Le modèle de pont est un modèle de conception utilisé en génie logiciel qui vise à « découpler une abstraction de son implémentation afin que les deux puissent varier indépendamment » , introduit par le Gang des Quatre . Le pont utilise l'encapsulation , l'agrégation et peut utiliser l'héritage pour séparer les responsabilités dans différentes classes .
Lorsqu'une classe varie fréquemment, les fonctionnalités de la programmation orientée objet deviennent très utiles car il est possible de modifier facilement le code d' un programme avec un minimum de connaissances préalables. Le modèle de conception « pont » est utile lorsque la classe et son comportement varient fréquemment. La classe elle-même peut être vue comme l' abstraction et ses fonctionnalités comme l' implémentation . Le modèle « pont » peut également être perçu comme deux niveaux d'abstraction.
Lorsqu'il n'existe qu'une seule implémentation fixe, ce modèle est connu sous le nom d'idiome Pimpl dans le monde C++ .
Le modèle de pont est souvent confondu avec le modèle d'adaptateur , et est souvent implémenté à l'aide du modèle d'adaptateur d'objet ; par exemple, dans le code Java ci-dessous.
Variante : L'implémentation peut être encore plus découplée en différant sa présence jusqu'au moment où l'abstraction est utilisée .
modèles de conception GoF bien connus qui décrivent comment résoudre des problèmes de conception récurrents pour concevoir des logiciels orientés objet flexibles et réutilisables, c'est-à-dire des objets plus faciles à implémenter, à modifier, à tester et à réutiliser.Quels problèmes le modèle de conception Bridge peut-il résoudre ?
- Une abstraction et son implémentation doivent être définies et étendues indépendamment l'une de l'autre.
- Il convient d'éviter une liaison au moment de la compilation entre une abstraction et son implémentation afin de pouvoir sélectionner une implémentation lors de l'exécution.
En utilisant l'héritage, différentes sous-classes implémentent une classe abstraite de manières différentes. Cependant, une implémentation est liée à l'abstraction lors de la compilation et ne peut être modifiée à l'exécution.
Quelle solution le modèle de conception Bridge décrit-il ?
- Séparez une abstraction (
Abstraction) de son implémentation (Implementor) en les plaçant dans des hiérarchies de classes distinctes. - Implémentez-le
Abstractionen termes de (en déléguant à) unImplementorobjet.
Cela permet de configurer un Abstractionobjet Implementorlors de l'exécution. Voir également le diagramme de classes et de séquence UML ci-dessous.
Structure
Diagramme de classes et de séquence UML

Dans le diagramme de classes UML ci-dessus , une abstraction Abstractionn'est pas implémentée de manière classique dans une hiérarchie d'héritage unique. Au lieu de cela, il existe une hiérarchie pour l'abstraction Abstractionet une hiérarchie distincte pour son implémentation Implementor, ce qui les rend indépendantes. L' Abstractioninterface operation()est implémentée en déléguant à l' Implementorinterface imp.operationImp(). Le diagramme de séquence UML illustre les interactions à l'exécution : l' objet délègue l'implémentation à l' objet (en appelant ) , qui effectue l'opération et retourne à .Abstraction1Implementor1operationImp()Implementor1Abstraction1
Diagramme de classes
![]()
- Abstraction (classe abstraite)
- définit l'interface abstraite
- conserve la référence de l'implémenteur.
- Abstraction raffinée (classe normale)
- étend l'interface définie par Abstraction
- Implémenteur (interface)
- définit l'interface pour les classes d'implémentation
- ConcreteImplementor (classe normale)
- implémente l'interface Implementor

Exemple
C#
Le modèle Bridge organise les objets dans une structure arborescente. Il découple l'abstraction de l'implémentation. Ici, l'abstraction représente le client à partir duquel les objets seront appelés. Un exemple implémenté en C# est donné ci-dessous.
Les classes Bridge constituent l'implémentation qui utilise la même architecture orientée interface pour créer des objets. L'abstraction, quant à elle, prend une instance de la classe d'implémentation et exécute sa méthode. Elles sont donc totalement découplées l'une de l'autre.
Cristal
Sortir
, x , y , radius ); } };class DrawingAPI02 : public DrawingAPI { public : [[ nodiscard ]] string drawCircle ( float x , float y , float radius ) const override { return std :: format ( "API02.cercle à {}:{} - rayon : {}" , x , y , radius ); } };class Shape { protected : const DrawingAPI & drawingApi ; public : Shape ( const DrawingAPI & api ) : drawingApi { api } {}virtuel ~ Forme () = par défaut ;virtual string draw () const = 0 ; virtual float resizeByPercentage ( const float percent ) noexcept = 0 ; };class 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 ()); }retourner 0 ; }
Sortir:
Java suivant définit un compte bancaire qui sépare les opérations sur le compte de l'enregistrement de ces opérations.System.out.printf(\"info: %s%n\", message); } static Logger warning() { return message -> System.out.printf(\"warning: %s%n\", message); } } abstract class AbstractAccount { private Logger logger = Logger.info(); public void setLogger(Logger logger) { this.logger = logger; } // the logging part is delegated to the Logger implementation protected void operate(String message, boolean result) { logger.log(String.format(\"%s result %s\", message, result)); } } class SimpleAccount extends 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()) { // you can also change the Logger implementation at runtime account.setLogger(Logger.warning()); } account.withdraw(10); account.withdraw(100); } } "paquet org.wikipedia.examples ;// Logger possède deux implémentations : info et 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 ); } }abstract class AbstractAccount { private Logger logger = Logger.info (); public void setLogger ( Logger logger ) { this.logger = logger ; } // la partie journalisation est déléguée à l'implémentation de Logger protected void opera ( String message , boolean result ) { logger.log ( String.format ( " % s résultat %s " , message , result ) ) ; } }class SimpleAccount extends 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 ( "retrait %s" , amount , shouldPerform )); } }public class BridgeDemo { public static void main ( String [] args ) { SimpleAccount account = new SimpleAccount ( 100 ); account . withdraw ( 75 ); if ( account . isBalanceLow ()) { // vous pouvez également modifier l'implémentation du Logger à l'exécution account . setLogger ( Logger . warning ()); } account . withdraw ( 10 ); account . withdraw ( 100 ); } }Le résultat sera :
$x : $y rayon $radius . " ; } }class DrawingAPI2 implements DrawingAPI { public function drawCircle ( $x , $y , $radius ) { echo "API2.cercle à $x : $y rayon $radius . " ; } }classe abstraite Forme { protégé $drawingAPI ;fonction abstraite publique dessiner (); fonction abstraite publique redimensionnerParPourcentage ( $pct );protected function __construct ( DrawingAPI $drawingAPI ) { $this -> drawingAPI = $drawingAPI ; } }classe CircleShape étend Shape { privé $x ; privé $y ; privé $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 ); }public function resizeByPercentage ( $pct ) { $this -> radius *= $pct ; } }class Tester { public static function main () { $shapes = array ( new CircleShape ( 1 , 3 , 7 , new DrawingAPI1 ()), new CircleShape ( 5 , 7 , 11 , new DrawingAPI2 ()), );foreach ( $shapes as $shape ) { $shape -> resizeByPercentage ( 2.5 ); $shape -> draw (); } } }Testeur :: principal ();
Sortir:
API1.cercle à 1:3 rayon 17,5 API2.cercle à 5:7 rayon 27,5
Scala
trait DrawingAPI { def drawCircle ( x : Double , y : Double , radius : Double ) }class DrawingAPI1 extends DrawingAPI { def drawCircle ( x : Double , y : Double , radius : Double ) = println ( s"API #1 $ x $ y $ radius " ) }class DrawingAPI2 extends DrawingAPI { def drawCircle ( x : Double , y : Double , radius : Double ) = println ( s"API #2 $ x $ y $ radius " ) }classe abstraite Forme ( drawingAPI : DrawingAPI ) { def draw () def resizePercentage ( pct : Double ) }class CircleShape ( x : Double , y : Double , var radius : Double , drawingAPI : DrawingAPI ) extends Shape ( drawingAPI : DrawingAPI ) {def draw ( ) = drawingAPI.drawCircle ( x , y , radius )def resizePercentage ( pct : Double ) { radius *= pct } }objet 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 () } } }
Python
Exemple de modèle Bridge. from abc import ABCMeta , abstractmethod from typing import NoReturnNON_IMPLÉMENTÉ : str = "Vous devriez implémenter ceci."classe DrawingAPI : __metaclass__ = ABCMeta@abstractmethod def draw_circle ( self , x : float , y : float , radius : float ) -> NoReturn : raise NotImplementedError ( NOT_IMPLEMENTED )class DrawingAPI1 ( DrawingAPI ): def draw_circle ( self , x : float , y : float , radius : float ) -> str : return f "API1.circle à { x } : { y } - rayon : { radius } "class DrawingAPI2 ( DrawingAPI ): def draw_circle ( self , x : float , y : float , radius : float ) -> str : return f "API2.circle à { x } : { y } - rayon : { radius } "class DrawingAPI3 ( DrawingAPI ): def draw_circle ( self , x : float , y : float , radius : float ) -> str : return f "API3.circle à { x } : { y } - rayon : { radius } "classe Shape : __metaclass__ = 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 )class CircleShape ( Shape ): 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 resize_by_percentage ( self , percent : float ) - > None : self.radius * = 1 + percent / 100class 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 ()), ]for shape in shapes : shape . resize_by_percentage ( 2.5 ) print ( shape . draw ())si __name__ == "__main__" : BridgePattern . test ()