Bridge
The bridge pattern
The bridge pattern is a structural pattern, it's a little like the Adapter Pattern, but with pre-planning on the design.You've recognised that different abstractions will have to call other abstractions and you want to be able to change each abstraction when required, so you build an abstraction, that contains another abstraction (called the implementation!):
abstract class Trance {
// another abstract class that I can use
protected Pontoon pontoon;
public abstract void Bond();
}
And the Pontoon implementor:
// another abstract class that I can use
protected Pontoon pontoon;
public abstract void Bond();
}
abstract class Pontoon {
public abstract void Scaffold();
}
Now I'm free to change the abstractions but I can always bridge to implementation.public abstract void Scaffold();
}
We make a concrete implementation of Trance:
class XTrance : Trance {
public XTrance(Pontoon pontoon) {
this.pontoon = pontoon;
}
public override void Bond() {
Console.WriteLine("XTrance bond called.");
pontoon.Scaffold();
}
}
I already know that inside XTrance's Bond method
I need to call the Scaffold method of my implementation Pontoon.
I now implement a version of Pontoon for XTrance:
public XTrance(Pontoon pontoon) {
this.pontoon = pontoon;
}
public override void Bond() {
Console.WriteLine("XTrance bond called.");
pontoon.Scaffold();
}
}
class Span : Pontoon {
private string name;
public Span(string name) {
this.name = name;
}
public override void Scaffold() {
Console.WriteLine("Span with name:{0}, Scaffold used.", name);
}
}
Running in a console with:
private string name;
public Span(string name) {
this.name = name;
}
public override void Scaffold() {
Console.WriteLine("Span with name:{0}, Scaffold used.", name);
}
}
static void Main(string[] args) {
Pontoon span = new Span("Alfred");
Trance xTrance = new XTrance(span);
xTrance.Bond();
Console.ReadKey();
}
Gives the output:
Pontoon span = new Span("Alfred");
Trance xTrance = new XTrance(span);
xTrance.Bond();
Console.ReadKey();
}
XTrance bond called.
Span with name:Alfred, Scaffold used.
Span with name:Alfred, Scaffold used.
I also know that sometimes it needs to use a different implementor, so I build:
class Gangplank : Pontoon {
public override void Scaffold() {
Console.WriteLine("Gangplank Scaffold called");
}
}
Giving it a test with:
public override void Scaffold() {
Console.WriteLine("Gangplank Scaffold called");
}
}
static void Main(string[] args) {
Pontoon span = new Span("Alfred");
Trance xTrance = new XTrance(span);
xTrance.Bond();
xTrance = new XTrance(new Gangplank());
xTrance.Bond();
Console.ReadKey();
}
Gives the console output:
Pontoon span = new Span("Alfred");
Trance xTrance = new XTrance(span);
xTrance.Bond();
xTrance = new XTrance(new Gangplank());
xTrance.Bond();
Console.ReadKey();
}
XTrance bond called.
Span with name:Alfred, Scaffold used.
XTrance bond called.
Gangplank Scaffold called
Span with name:Alfred, Scaffold used.
XTrance bond called.
Gangplank Scaffold called
Great all working I can add implementors at will, I realise I need another abstraction but could use one of my implementors.
So I code:
class YTrance : Trance {
public YTrance(Pontoon pontoon) {
this.pontoon = pontoon;
}
public override void Bond() {
Console.WriteLine("YTrance bond called.");
pontoon.Scaffold();
}
}
My console test code:
public YTrance(Pontoon pontoon) {
this.pontoon = pontoon;
}
public override void Bond() {
Console.WriteLine("YTrance bond called.");
pontoon.Scaffold();
}
}
static void Main(string[] args) {
Pontoon span = new Span("Berty");
Trance yTrance = new YTrance(span);
yTrance.Bond();
Console.ReadKey();
}
My output being:
Pontoon span = new Span("Berty");
Trance yTrance = new YTrance(span);
yTrance.Bond();
Console.ReadKey();
}
YTrance bond called.
Span with name:Berty, Scaffold used.
Span with name:Berty, Scaffold used.
Pretty easy stuff but care has to be taken as one implementor could be used for many abstractions and this is no problem if it's immutable but if it's not could lead to a race condition.
Example:
static void Main(string[] args) {
Pontoon span = new Span("Berty");
Trance yTrance = new YTrance(span);
yTrance.Bond();
// same instance of Span!
Trance yTrance = new YTrance(span);
yTrance.Bond();
Console.ReadKey();
}
Giving the output:
Pontoon span = new Span("Berty");
Trance yTrance = new YTrance(span);
yTrance.Bond();
// same instance of Span!
Trance yTrance = new YTrance(span);
yTrance.Bond();
Console.ReadKey();
}
XTrance bond called.
Span with name:Alfred, Scaffold used.
YTrance bond called.
Span with name:Alfred, Scaffold used.
Span with name:Alfred, Scaffold used.
YTrance bond called.
Span with name:Alfred, Scaffold used.
Summary
The bridge pattern is a useful pattern if abstractions or implementations have to be changed, as
it lowers the coupling between the interacting objects but care needs to be taken not to feed
a mutable implementor to a manipulating abstraction as this would be thread unsafe.