Bridge pattern
From Wikipedia, the free encyclopedia
The bridge pattern is a design pattern used in software engineering which is meant to "decouple an abstraction from its implementation so that the two can vary independently" (Gamma et. al.). The bridge uses encapsulation, aggregation, and can use inheritance to separate responsibilities into different classes.
When a class varies often, the features of object-oriented programming become very useful because changes to a program's code can be made easily with minimal prior knowledge about the program. The bridge pattern is useful when not only the class itself varies often but also what the class does. The class itself can be thought of as the abstraction and what the class can do as the implementation.
Contents |
[edit] Non-Technical Examples
[edit] Shape Abstraction
When the abstraction and implementation are separated they can vary independently. Consider an abstraction such as shapes. There are many types of shapes and each with its own properties but there are things that all shapes do. One thing all shapes can do is draw themselves. However, drawing graphics to a screen can sometimes be dependent on different graphics implementations or operating systems. Shapes have to be able to be drawn on many types of systems, but having the shape itself implement them all or modifying the shape class to work with different architectures is not practical. The bridge helps by allowing the creation of new classes that provide the drawing implementation. The abstract - shape - class provides methods for getting the size or properties of a shape while the implementation - drawing - class provides an interface for drawing graphics. Now if a new shape needs to be created or there is a new graphics API to be drawn on, then it is very easy to add a new class that implements the needed features. <ref>Shalloway, Trott. Design Patterns Explained: A New Perspective on Object-Oriented Design.</ref>
[edit] Car Abstraction
Imagine two types of cars, a Jaguar and a Mercedes. The car is the Abstraction, the Jaguar is an Abstraction Refinement and so is the Mercedes. The Abstraction defines that a Car has tires and an engine and so forth. The Abstraction Refinements decide what kind of tires and engine they exactly have.
Imagine two types of road. The road is the Implementor (see image below). A highway and an interstate highway are the Implementation Details. Both car-types need to be able to drive on both road-types; this concept is what the Bridge Pattern is all about.
[edit] Structure
- Client
- The Object using the bridge pattern
- Abstraction
- defines the abstract interface
- maintains the implementor reference
- Refined Abstraction
- Extends the interface defined by Abstraction
- Implementor
- defines the interface for implementation classes. (Typically the Abstraction interface defines higher level operations based on this interface operations)
- ConcreteImplementor
- implements the Implementor interface
[edit] Code Examples
[edit] Java
The following Java program illustrates the 'shape' example given above and will output:
API1.circle at 1.000000:2.000000 radius 7.500000 API2.circle at 5.000000:7.000000 radius 27.500000
import java.util.*;
/** "Implementor" */
interface DrawingAPI {
public void drawCircle(double x, double y, double radius);
}
/** "ConcreteImplementor" 1/2 */
class DrawingAPI1 implements DrawingAPI {
public void drawCircle(double x, double y, double radius)
{ System.out.printf("API1.circle at %f:%f radius %f\n", x, y, radius); }
}
/** "ConcreteImplementor" 2/2 */
class DrawingAPI2 implements DrawingAPI {
public void drawCircle(double x, double y, double radius)
{ System.out.printf("API2.circle at %f:%f radius %f\n", x, y, radius); }
}
/** "Abstraction" */
interface Shape {
public void draw(); // low-level
public void resizeByPercentage(double pct); // high-level
}
/** "Refined Abstraction" */
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;
}
// low-level i.e. Implementation specific
public void draw() { drawingAPI.drawCircle(x, y, radius); }
// high-level i.e. Abstraction specific
public void resizeByPercentage(double pct) { radius *= pct; }
}
/** "Client" */
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();
}
}
}
[edit] C++
Notice that a circle can be drawn by either of the following two (drawing) API's. But also a square can be drawn by both API's. The drawing API's are simulated as output to console.
#include <iostream>
using namespace std; //For brevity.
//Implementor class DrawingAPI { public: virtual void DrawCircle(const double x, const double y, const double radius) = 0; virtual void DrawSquare(const double x, const double y) = 0; };
// ConcreteImplementor 1/2 class OpenGL : public DrawingAPI { public: void DrawCircle(const double x, const double y, const double radius) { cout<<"OpenGL based circle drawn at "<<x<<":"<<y<<" radius "<<radius<<endl; } void DrawSquare(const double x, const double y) { cout<<"OpenGL based square drawn at "<<x<<":"<<y<<endl; } };
// ConcreteImplementor 2/2 class DirectX : public DrawingAPI { public: void DrawCircle(const double x, const double y, const double radius) { cout<<"DirectX based circle drawn at "<<x<<":"<<y<<" radius "<<radius<<endl; } void DrawSquare(const double x, const double y) { cout<<"DirectX based square drawn at "<<x<<":"<<y<<endl; } };
//Abstraction" class Shape { public: //Constructor Shape(double x, double y, double radius, DrawingAPI* da) : _x(x), _y(y), _radius(radius), _da(da) { } Shape(double x, double y, DrawingAPI* da) : _x(x), _y(y), _da(da) { } public: // low-level virtual void Draw() = 0; protected: void DrawCircle() { _da->DrawCircle(_x, _y, _radius); } void DrawSquare() { _da->DrawSquare(_x, _y); } // high-level // This needs to be refined in the Refined Abstraction classes. virtual void ResizeByPercentage(double const pct) = 0; protected: double _x, _y, _radius; DrawingAPI* _da; // Shapes could be displayed by different DrawingAPI's. };
//Refined Abstraction: define a Circle.
class Circle : public Shape
{
public : //Constructor
Circle(double x, double y, double radius, DrawingAPI* da) :
Shape(x, y, radius, da)
{ }
public: //Behavior
// low-level i.e. Implementation specific.
// Draw doesn't even know what parameters it's using and WHO is drawing it.
void Draw() { DrawCircle(); }
// High-level i.e. Abstraction specific.
// A circle has a radius, but a square has a length (for example).
void ResizeByPercentage(const double pct) { _radius *= pct; }
};
//Refined Abstraction: define a Square.
class Square : public Shape
{
public : //Constructor
Square(double x, double y, DrawingAPI* da) :
Shape(x, y, da)
{ }
public: //Behavior
// Low-level i.e. Implementation specific.
// Draw doesn't even know what parameters it's using and WHO is drawing it.
void Draw() { DrawSquare(); }
// High-level i.e. Abstraction specific.
// A circle has a radius, but a square has a length (for example).
void ResizeByPercentage(const double pct) { _x *= pct; }
};
//The Client.
// -->> int main() merely demonstrates the interchangebility.
int main()
{
Shape* TheShape[4]; //2 shapes x 2 implementations = 4 client-options.
DrawingAPI* DA[2]; //2 Implementations.
DA[0] = new OpenGL;
DA[1] = new DirectX;
TheShape[0] = new Circle(1, 2, 7.5, DA[0]); //Draw circle with OpenGL
TheShape[0]->Draw();
TheShape[1] = new Circle(5, 7, 27.5, DA[1]); //Draw circle with DirectX
TheShape[1]->Draw();
TheShape[2] = new Square(10, 14, DA[0]); //Draw square with OpenGL
TheShape[2]->Draw();
TheShape[3] = new Square(2.9, 80, DA[1]); //Draw square with DirectX
TheShape[3]->Draw();
//Give back allocated memory.
delete DA[0], DA[1];
delete TheShape[0], TheShape[1], TheShape[2], TheShape[3];
return 0;
}
[edit] C#
The following C# program illustrates the 'shape' example given above and will output:
API1.circle at 1:2 radius 7.5 API2.circle at 5:7 radius 27.5
using System;
/** "Implementor" */
interface DrawingAPI {
void DrawCircle(double x, double y, double radius);
}
/** "ConcreteImplementor" 1/2 */
class DrawingAPI1 : DrawingAPI {
public void DrawCircle(double x, double y, double radius)
{
System.Console.WriteLine("API1.circle at {0}:{1} radius {2}\n", x, y, radius);
}
}
/** "ConcreteImplementor" 2/2 */
class DrawingAPI2 : DrawingAPI
{
public void DrawCircle(double x, double y, double radius)
{
System.Console.WriteLine("API2.circle at {0}:{1} radius {2}\n", x, y, radius);
}
}
/** "Abstraction" */
interface Shape {
void Draw(); // low-level
void ResizeByPercentage(double pct); // high-level
}
/** "Refined Abstraction" */
class CircleShape : 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;
}
// low-level i.e. Implementation specific
public void Draw() { drawingAPI.DrawCircle(x, y, radius); }
// high-level i.e. Abstraction specific
public void ResizeByPercentage(double pct) { radius *= pct; } }
/** "Client" */
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());
foreach (Shape shape in shapes) {
shape.ResizeByPercentage(2.5);
shape.Draw();
}
}
}
[edit] Perl
The following Perl program illustrates the 'shape' example given above and will output:
API1.circle at 1:2 radius 7.5 API2.circle at 5:7 radius 27.5
### ConcreteImplementor 1/2
package DrawingAPI1;
sub new {
my $class = shift;
return bless({}, $class);
}
sub draw_circle {
my($self, $x, $y, $radius) = @_;
print("API1.circle at $x:$y radius $radius\n");
}
1;
### ConcreteImplementor 2/2
package DrawingAPI2;
sub new {
my $class = shift;
return bless({}, $class);
}
sub draw_circle {
my($self, $x, $y, $radius) = @_;
print("API2.circle at $x:$y radius $radius\n");
}
1;
### Refined Abstraction
package CircleShape;
sub new {
my $class = shift;
my ($x, $y, $radius, $drawing_api) = @_;
my $self = bless({
x => $x,
y => $y,
radius => $radius,
drawing_api => $drawing_api,
}, $class);
return $self;
}
sub draw {
my $self = shift;
$self->{drawing_api}->draw_circle(
$self->{x}, $self->{y}, $self->{radius});
}
sub resize_by_percentage {
my($self, $percent) = @_;
$self->{radius} *= $percent;
}
1;
### client
my @shapes = (
CircleShape->new(1, 2, 3, DrawingAPI1->new),
CircleShape->new(5, 7, 11, DrawingAPI2->new),
);
foreach my $shape (@shapes) {
$shape->resize_by_percentage(2.5);
$shape->draw();
}
[edit] PHP
Note that this example uses features available only in PHP 5 and up. It will not work in PHP 4.
#!/usr/local/bin/php
<?php
/** "Implementor" */
interface DrawingAPI {
public function drawCircle($x, $y, $radius);
}
/** "ConcreteImplementor" 1/2 */
class DrawingAPI1 implements DrawingAPI {
public function drawCircle($x, $y, $radius)
{ echo "API1.circle at $x:$y radius $radius\n"; }
}
/** "ConcreteImplementor" 2/2 */
class DrawingAPI2 implements DrawingAPI {
public function drawCircle($x, $y, $radius)
{ echo "API2.circle at $x:$y radius $radius\n"; }
}
/** "Abstraction" */
interface Shape {
public function draw(); // low-level
public function resizeByPercentage($pct); // high-level
}
/** "Refined Abstraction" */
class CircleShape implements Shape {
private $x;
private $y;
private $radius;
private $drawingAPI;
public function __construct($x, $y, $radius, DrawingAPI $drawingAPI) {
$this->x = $x;
$this->y = $y;
$this->radius = $radius;
$this->drawingAPI = $drawingAPI;
}
// low-level i.e. Implementation specific
public function draw() { $this->drawingAPI->drawCircle($this->x, $this->y, $this->radius); }
// high-level i.e. Abstraction specific
public function resizeByPercentage($pct) { $this->radius *= $pct; }
}
/** "Client" */
$shapes = array();
$shapes[] = new CircleShape(1, 2, 3, new DrawingAPI1());
$shapes[] = new CircleShape(5, 7, 11, new DrawingAPI2());
foreach ($shapes as $shape) {
$shape->resizeByPercentage(2.5);
$shape->draw();
}
[edit] Python
The following Python program illustrates the 'shape' example given above and will output:
API1.circle at 1.000000:2.000000 radius 7.500000 API2.circle at 5.000000:7.000000 radius 27.500000
class CircleShape:
def __init__(self,x,y,r,da):
self.x = x
self.y = y
self.r = r
self.da = da
def draw(self): self.da.drawCircle(self.x,self.y,self.r)
def resizeByPercentage(self,pct): self.r *= pct
class DrawingAPI2:
def drawCircle(self,x,y,r):
print "API2.circle at %f:%f radius %f" % (x,y,r)
class DrawingAPI1:
def drawCircle(self,x,y,r):
print "API1.circle at %f:%f radius %f" % (x,y,r)
if __name__ == '__main__':
shapes = [CircleShape(1,2,3,DrawingAPI1()), CircleShape(5,7,11,DrawingAPI2())]
for shape in shapes:
shape.resizeByPercentage(2.5)
shape.draw()
[edit] Ruby
This will print:
API1.circle at 1.000000:2.000000 radius 7.500000 API2.circle at 5.000000:7.000000 radius 27.500000
class CircleShape
def initialize(x, y, r, da)
@x = x
@y = y
@r = r
@da = da
end
def draw; @da.drawCircle(@x, @y, @r); end
def resizeByPercentage(pct); @r *= pct; end
end
class DrawingAPI1
def drawCircle(x,y,r)
printf "API1.circle at %f:%f radius %f", x, y, r
end
end
class DrawingAPI2
def drawCircle(x,y,r)
printf "API2.circle at %f:%f radius %f", x, y, r
end
end
shapes = [CircleShape.new(1, 2, 3, DrawingAPI1.new), CircleShape.new(5, 7, 11, DrawingAPI2.new)]
shapes.each do |shape|
shape.resizeByPercentage(2.5)
shape.draw
end
This is translated from the Python example. DrawingAPI1 and DrawingAPI2 can be implemented more elegantly as blocks.
[edit] See also
[edit] References
<references/>
Creational: Abstract factory • Builder • Factory • Prototype • Singleton
Structural: Adapter • Bridge • Composite • Decorator • Façade • Flyweight • Proxy
Behavorial: Chain of responsibility • Command • Interpreter • Iterator • Mediator • Memento • Observer • State • Strategy • Template method • Visitor
es:Bridge (patrón de diseño) fr:Adaptateur (motif de conception) pl:Wzorzec mostu pt:Bridge (padrão) vi:Bridge pattern

