Patrons de conception/Pont
Patron de conception | |
---|---|
Catégorie : « Gang of Four » – Structure | |
Nom français : | Pont |
Nom anglais : | Bridge |
Découpler l'interface d'une classe de son implémentation |
Le pont est un patron de conception qui permet de découpler l'interface d'une classe et son implémentation. Ainsi l'interface et l'implémentation peuvent varier séparément.
Attention, à ne pas confondre ce patron avec l'adaptateur. En effet, l'adaptateur est utilisé pour adapter l'interface d'une classe vers une autre interface (donc pour faire en sorte que l'interface d'une ou plusieurs classes ressemble à l'interface d'une classe en particulier).
Le pont est lui utilisé pour découpler l'interface de l'implémentation. Ainsi, vous pouvez modifier ou changer l'implémentation d'une classe sans devoir modifier le code client (si l'interface ne change pas bien entendu).
Exemple : formes géométriques
[modifier | modifier le wikicode]Considérons une classe représentant la classe de base de formes géométriques, et ses classes (cercles, rectangles, triangles, ...). Tous les types de formes ont des propriétés communes (une couleur par exemple) et des méthodes abstraites communes (calcul de surface par exemple) implémentées par les classes dérivées (comment calculer la surface d'un cercle, ...).
Toutes les formes peuvent également se dessiner à l'écran. Mais la façon de dessiner dépend de l'environnement graphique et du système d'exploitation. Plutôt que d'ajouter une méthode par environnement possible à chacune des formes, le patron de conception Pont suggère de créer une interface séparée pour les primitives de dessin. Cette interface est utilisée par les différentes formes qui alors ne dépendent pas de l'implémentation.
Diagramme de classes UML
[modifier | modifier le wikicode]Le patron de conception Pont peut être représenté par le diagramme de classes UML suivant :
Exemples de code
[modifier | modifier le wikicode]Le programme Java 5 ci-dessous illustre l'exemple des formes géométriques donné précédemment et affiche :
API1.cercle position 1.000000:2.000000 rayon 7.500000 API2.cercle position 5.000000:7.000000 rayon 27.500000
/** "Implémentation" */
interface DrawingAPI
{
public void drawCircle(double x, double y, double radius);
}
/** "Implémentation1" */
class DrawingAPI1 implements DrawingAPI
{
public void drawCircle(double x, double y, double radius)
{
System.out.printf("API1.cercle position %f:%f rayon %f\n", x, y, radius);
}
}
/** "Implémentation2" */
class DrawingAPI2 implements DrawingAPI
{
public void drawCircle(double x, double y, double radius)
{
System.out.printf("API2.cercle position %f:%f rayon %f\n", x, y, radius);
}
}
/** "Abstraction" */
interface Shape
{
public void draw(); // bas niveau
public void resizeByPercentage(double pct); // haut niveau
}
/** "AbstractionRaffinée" */
class CircleShape implements Shape
{
private double x, y, radius;
private DrawingAPI drawingAPI;
public CircleShape(double x, double y, double radius, DrawingAPI drawingAPI)
{
this.x = x; this.y = y; this.radius = radius;
this.drawingAPI = drawingAPI;
}
// bas niveau, càd spécifique à une implémentation
public void draw()
{
drawingAPI.drawCircle(x, y, radius);
}
// haut niveau, càd spécifique à l'abstraction
public void resizeByPercentage(double pct)
{
radius *= pct;
}
}
/** Classe utilisatrice */
class BridgePattern
{
public static void main(String[] args)
{
Shape[] shapes = new Shape[2];
shapes[0] = new CircleShape(1, 2, 3, new DrawingAPI1());
shapes[1] = new CircleShape(5, 7, 11, new DrawingAPI2());
for (Shape shape : shapes)
{
shape.resizeByPercentage(2.5);
shape.draw();
}
}
}
interface DrawingAPI {
function drawCircle($dX, $dY, $dRadius);
}
class DrawingAPI1 implements DrawingAPI {
public function drawCircle($dX, $dY, $dRadius) {
echo "API1.circle at ".$dX.":".$dY." radius ".$dRadius."<br/>";
}
}
class DrawingAPI2 implements DrawingAPI {
public function drawCircle($dX, $dY, $dRadius) {
echo "API2.circle at ".$dX.":".$dY." radius ".$dRadius."<br/>";
}
}
abstract class Shape {
protected $oDrawingAPI;
public abstract function draw();
public abstract function resizeByPercentage($dPct);
protected function __construct(DrawingAPI $oDrawingAPI) {
$this->oDrawingAPI = $oDrawingAPI;
}
}
class CircleShape extends Shape {
private $dX;
private $dY;
private $dRadius;
public function __construct(
$dX, $dY,
$dRadius,
DrawingAPI $oDrawingAPI
) {
parent::__construct($oDrawingAPI);
$this->dX = $dX;
$this->dY = $dY;
$this->dRadius = $dRadius;
}
public function draw() {
$this->oDrawingAPI->drawCircle(
$this->dX,
$this->dY,
$this->dRadius
);
}
public function resizeByPercentage($dPct) {
$this->dRadius *= $dPct;
}
}
class Tester {
public static function main() {
$aShapes = array(
new CircleShape(1, 3, 7, new DrawingAPI1()),
new CircleShape(5, 7, 11, new DrawingAPI2()),
);
foreach ($aShapes as $shapes) {
$shapes->resizeByPercentage(2.5);
$shapes->draw();
}
}
}
Tester::main();
Output:
API1.circle at 1:3 radius 17.5 API2.circle at 5:7 radius 27.5
class Abstraction
def initialize(implementor)
@implementor = implementor
end
def operation
raise 'Implementor object does not respond to the operation method' unless @implementor.respond_to?(:operation)
@implementor.operation
end
end
class RefinedAbstraction < Abstraction
def operation
puts 'Starting operation...'
super
end
end
class Implementor
def operation
puts 'Doing neccessary stuff'
end
end
class ConcreteImplementorA < Implementor
def operation
super
puts 'Doing additional stuff'
end
end
class ConcreteImplementorB < Implementor
def operation
super
puts 'Doing other additional stuff'
end
end
normal_with_a = Abstraction.new(ConcreteImplementorA.new)
normal_with_a.operation
# Doing neccessary stuff
# Doing additional stuff
normal_with_b = Abstraction.new(ConcreteImplementorB.new)
normal_with_b.operation
# Doing neccessary stuff
# Doing other additional stuff
refined_with_a = RefinedAbstraction.new(ConcreteImplementorA.new)
refined_with_a.operation
# Starting operation...
# Doing neccessary stuff
# Doing additional stuff
refined_with_b = RefinedAbstraction.new(ConcreteImplementorB.new)
refined_with_b.operation
# Starting operation...
# Doing neccessary stuff
# Doing other additional stuff