web-dev-qa-db-fra.com

Quand utilisez-vous le modèle de pont? En quoi est-ce différent du motif adaptateur?

Quelqu'un a-t-il déjà utilisé le Bridge Pattern dans une application réelle? Si oui, comment l'avez-vous utilisé? Est-ce moi, ou est-ce juste le Pattern Adaptateur avec une petite injection de dépendance jeté dans le mix? Est-ce qu'il mérite vraiment son propre modèle?

143
Charles Graham

Un exemple classique du modèle Bridge est utilisé dans la définition des formes dans un environnement d'interface utilisateur (voir le entrée Wikipedia de modèle Bridge ). Le modèle Bridge est un composite des modèles Modèle et Stratégie .

Il est courant de voir certains aspects du modèle d’adaptateur dans le modèle de pont. Cependant, pour citer cet article :

À première vue, le modèle Bridge ressemble beaucoup au modèle Adapter, dans le sens où une classe est utilisée pour convertir un type d'interface en un autre. Cependant, l'intention du modèle Adapter est de faire en sorte que l'interface d'une ou de plusieurs classes soit identique à celle d'une classe particulière. Le modèle Bridge est conçu pour séparer l'interface d'une classe de son implémentation afin que vous puissiez modifier ou remplacer l'implémentation sans changer le code client.

76
shek

Il y a une combinaison de réponses de Federico et de John .

Quand:

                   ----Shape---
                  /            \
         Rectangle              Circle
        /         \            /      \
BlueRectangle  RedRectangle BlueCircle RedCircle

Refactor à:

          ----Shape---                        Color
         /            \                       /   \
Rectangle(Color)   Circle(Color)           Blue   Red
235
Anton Shchastnyi

Le modèle Bridge est une application du vieux conseil "préférez la composition à l'héritage". Cela devient pratique lorsque vous devez sous-classer différents moments de manière orthogonale. Supposons que vous deviez mettre en place une hiérarchie de formes colorées. Vous ne sous-classeriez pas Shape with Rectangle and Circle puis sous-classe Rectangle avec RedRectangle, BlueRectangle et GreenRectangle et la même chose pour Circle, n'est-ce pas? Vous préféreriez dire que chaque forme a une couleur et mettre en œuvre une hiérarchie de couleurs, et c'est le motif de pont. Eh bien, je ne mettrais pas en place une "hiérarchie de couleurs", mais vous voyez l'idée ...

223

Quand:

        A
     /     \
    Aa      Ab
   / \     /  \
 Aa1 Aa2  Ab1 Ab2

Refactor à:

     A         N
  /     \     / \
Aa(N) Ab(N)  1   2
204
John Sonmez

Adaptateur et pont sont certainement liés, et la distinction est subtile. Il est probable que certaines personnes qui pensent utiliser l'un de ces modèles utilisent réellement l'autre modèle.

L’explication que j’ai vue est que l’adaptateur est utilisé lorsque vous essayez d’unifier les interfaces de certaines classes incompatibles qui existent déjà . L'adaptateur fonctionne comme une sorte de traducteur pour les implémentations qui pourraient être considérées héritées .

Considérant que le modèle Bridge est utilisé pour un code qui est plus susceptible d’être un greenfield. Vous concevez Bridge pour fournir une interface abstraite à une implémentation devant varier, mais vous définissez également l'interface de ces classes d'implémentation.

Les pilotes de périphérique sont un exemple souvent cité de Bridge, mais je dirais que c’est un pont si vous définissez les spécifications d’interface pour les fournisseurs de périphériques, mais c’est un adaptateur si vous utilisez les pilotes de périphérique existants et créez une classe wrapper. fournir une interface unifiée.

En termes de code, les deux modèles sont très similaires. Du point de vue commercial, ils sont différents.

Voir aussi http://c2.com/cgi/wiki?BridgePattern

29
Bill Karwin

D'après mon expérience, Bridge est un motif assez souvent récurrent, car c'est la solution à chaque fois que il y a deux dimensions orthogonales dans le domaine. Par exemple. formes et méthodes de dessin, comportements et plateformes, formats de fichier et sérialiseurs, etc.

Et un conseil: pensez toujours aux modèles de conception du point de vue conceptuel et non du point de vue de la mise en œuvre. Du bon point de vue, Bridge ne peut pas être confondu avec Adapter, car ils résolvent un problème différent, et la composition est supérieure à l'héritage, non pas en soi, mais parce qu'elle permet de gérer séparément les problèmes orthogonaux.

26
thSoft

L'intention de Bridge et Adaptateur est différente et nous avons besoin des deux modèles séparément .

Modèle de pont:

  1. C'est un motif structurel
  2. Abstraction et implémentation ne sont pas liées au moment de la compilation
  3. Abstraction et mise en œuvre - les deux peuvent varier sans impact sur le client
  4. Utilise la composition sur l'héritage.

Utilisez le motif Bridge lorsque:

  1. Vous voulez une liaison d'exécution de l'implémentation,
  2. Vous avez une prolifération de classes résultant d'une interface couplée et de nombreuses implémentations,
  3. Vous voulez partager une implémentation entre plusieurs objets,
  4. Vous devez mapper des hiérarchies de classes orthogonales.

@ John Sonmez répond clairement l’efficacité du modèle de pont pour réduire la hiérarchie des classes.

Vous pouvez vous référer au lien ci-dessous pour en savoir plus sur le modèle de pont avec un exemple de code.

Modèle d'adaptateur:

  1. Cela permet à deux interfaces non liées de fonctionner ensemble à travers les différents objets, jouant éventuellement le même rôle.
  2. Il modifie l'interface d'origine.

Principales différences:

  1. Adaptateur fait fonctionner les choses après leur conception; Bridge les fait fonctionner avant d’être.
  2. Bridge est conçu pour permettre à l'abstraction et à la mise en oeuvre de varier indépendamment. Adapter est installé ultérieurement pour que les classes non apparentées fonctionnent ensemble.
  3. L'intention: Adapter permet à deux interfaces non liées de fonctionner ensemble. Bridge permet à l'abstraction et à la mise en œuvre de varier de manière indépendante.

Question SE associée avec diagramme UML et code de travail:

Différence entre modèle de pont et modèle d’adaptateur

Articles utiles:

pont de fabrication article de modèle

adaptateur de création de source article de modèle

journal de pont article de modèle

EDIT:

Exemple concret Bridge Pattern (Selon la suggestion de meta.stackoverflow.com, exemple de site de documentation incorporé dans cet article, car la documentation est envoyée à Sun-set)

Le modèle de pont dissocie l'abstraction de la mise en œuvre afin que les deux puissent varier indépendamment. Cela a été réalisé avec la composition plutôt que l'héritage.

Modèle de pont UML de Wikipedia:

Bridge pattern UML from Wikipedia

Vous avez quatre composants dans ce modèle.

Abstraction: définit une interface

RefinedAbstraction: Il implémente l'abstraction:

Implementor: définit une interface pour l'implémentation

ConcreteImplementor: Il implémente l'interface implémenteur.

The crux of Bridge pattern : Deux hiérarchies de classes orthogonales utilisant la composition (et pas d'héritage). La hiérarchie d'abstraction et la hiérarchie d'implémentation peuvent varier indépendamment. L'implémentation ne fait jamais référence à l'abstraction. Abstraction contient une interface d'implémentation en tant que membre (via la composition). Cette composition réduit un niveau supplémentaire de hiérarchie d'héritage.

Cas d'utilisation de Word réel:

Permet à différents véhicules d’avoir les deux versions du système de transmission manuelle et automatique.

Exemple de code:

/* Implementor interface*/
interface Gear{
    void handleGear();
}

/* Concrete Implementor - 1 */
class ManualGear implements Gear{
    public void handleGear(){
        System.out.println("Manual gear");
    }
}
/* Concrete Implementor - 2 */
class AutoGear implements Gear{
    public void handleGear(){
        System.out.println("Auto gear");
    }
}
/* Abstraction (abstract class) */
abstract class Vehicle {
    Gear gear;
    public Vehicle(Gear gear){
        this.gear = gear;
    }
    abstract void addGear();
}
/* RefinedAbstraction - 1*/
class Car extends Vehicle{
    public Car(Gear gear){
        super(gear);
        // initialize various other Car components to make the car
    }
    public void addGear(){
        System.out.print("Car handles ");
        gear.handleGear();
    }
}
/* RefinedAbstraction - 2 */
class Truck extends Vehicle{
    public Truck(Gear gear){
        super(gear);
        // initialize various other Truck components to make the car
    }
    public void addGear(){
        System.out.print("Truck handles " );
        gear.handleGear();
    }
}
/* Client program */
public class BridgeDemo {    
    public static void main(String args[]){
        Gear gear = new ManualGear();
        Vehicle vehicle = new Car(gear);
        vehicle.addGear();

        gear = new AutoGear();
        vehicle = new Car(gear);
        vehicle.addGear();

        gear = new ManualGear();
        vehicle = new Truck(gear);
        vehicle.addGear();

        gear = new AutoGear();
        vehicle = new Truck(gear);
        vehicle.addGear();
    }
}

sortie:

Car handles Manual gear
Car handles Auto gear
Truck handles Manual gear
Truck handles Auto gear

Explication:

  1. Vehicle est une abstraction.
  2. Car et Truck sont deux implémentations concrètes de Vehicle.
  3. Vehicle définit une méthode abstraite: addGear().
  4. Gear est l'interface d'implémentation
  5. ManualGear et AutoGear sont deux implémentations de Gear
  6. Vehicle contient implementor interface plutôt que de l'implémenter. Compositon de l'interface d'implémentation est le nœud de ce modèle: Il permet à l'abstraction et à l'implémentation de varier indépendamment.
  7. Car et Truck définissent une implémentation (abstraction redéfinie) pour l'abstraction: addGear(): Il contient Gear - soit Manual ou Auto

Cas d'utilisation du motif de pont :

  1. L'abstraction et La mise en oeuvre peut changer indépendamment l'une de l'autre et ils ne sont pas liés à temps de compilation
  2. Cartographie des hiérarchies orthogonales - Une pour Abstraction et une pour Mise en oeuvre.
20
Ravindra babu

J'ai utilisé le modèle de pont au travail. Je programme en C++, où on l'appelle souvent l'idiome PIMPL (pointeur sur l'implémentation). Cela ressemble à ceci:

class A
{
public: 
  void foo()
  {
    pImpl->foo();
  }
private:
  Aimpl *pImpl;
};

class Aimpl
{
public:
  void foo();
  void bar();
};  

Dans cet exemple, class A Contient l'interface et class Aimpl Contient l'implémentation.

L'une des utilisations de ce modèle est d'exposer uniquement certains membres publics de la classe d'implémentation, mais pas les autres. Dans l'exemple, seul Aimpl::foo() peut être appelé via l'interface publique de A, mais pas Aimpl::bar()

Un autre avantage est que vous pouvez définir Aimpl dans un fichier d’en-tête séparé qui n’a pas besoin d’être inclus par les utilisateurs de A. Tout ce que vous avez à faire est d'utiliser une déclaration forward de Aimpl avant que A ne soit définie et de déplacer les définitions de toutes les fonctions membres faisant référence à pImpl dans le fichier .cpp. Cela vous permet de garder l'en-tête Aimpl privé et de réduire le temps de compilation.

9
Dima

Pour mettre un exemple de forme dans le code:

#include<iostream>
#include<string>
#include<cstdlib>

using namespace std;

class IColor
{
public:
    virtual string Color() = 0;
};

class RedColor: public IColor
{
public:
    string Color()
    {
        return "of Red Color";
    }
};

class BlueColor: public IColor
{
public:
    string Color()
    {
        return "of Blue Color";
    }
};


class IShape
{
public:
virtual string Draw() = 0;
};

class Circle: public IShape
{
        IColor* impl;
    public:
        Circle(IColor *obj):impl(obj){}
        string Draw()
        {
            return "Drawn a Circle "+ impl->Color();
        }
};

class Square: public IShape
{
        IColor* impl;
    public:
        Square(IColor *obj):impl(obj){}
        string Draw()
        {
        return "Drawn a Square "+ impl->Color();;
        }
};

int main()
{
IColor* red = new RedColor();
IColor* blue = new BlueColor();

IShape* sq = new Square(red);
IShape* cr = new Circle(blue);

cout<<"\n"<<sq->Draw();
cout<<"\n"<<cr->Draw();

delete red;
delete blue;
return 1;
}

La sortie est:

Drawn a Square of Red Color
Drawn a Circle of Blue Color

Notez la facilité avec laquelle de nouvelles couleurs et formes peuvent être ajoutées au système sans entraîner d'explosion de sous-classes en raison de permutations.

6
NotAgain

pour moi, je le considère comme un mécanisme permettant d’échanger des interfaces. Dans le monde réel, vous pourriez avoir une classe pouvant utiliser plusieurs interfaces, Bridge vous permet d’échanger.

0
j2emanue