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?
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.
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
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 ...
Quand:
A
/ \
Aa Ab
/ \ / \
Aa1 Aa2 Ab1 Ab2
Refactor à:
A N
/ \ / \
Aa(N) Ab(N) 1 2
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
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.
L'intention de Bridge et Adaptateur est différente et nous avons besoin des deux modèles séparément .
Modèle de pont:
Utilisez le motif Bridge lorsque:
@ 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:
Principales différences:
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:
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:
Vehicle
est une abstraction.Car
et Truck
sont deux implémentations concrètes de Vehicle
.Vehicle
définit une méthode abstraite: addGear()
.Gear
est l'interface d'implémentationManualGear
et AutoGear
sont deux implémentations de Gear
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.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 :
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.
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.
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.