Bridge
Decouples an abstraction from its implementation so that both can evolve independently.
Context
You have a hierarchy of shapes (circle, square) and a hierarchy of renderers (SVG, Canvas). If you cross them with inheritance, you get a combinatorial explosion: SvgCircle, CanvasCircle, SvgSquare...
Problem
- You couple two hierarchies that change for different reasons.
- Any new variant multiplies the number of classes.
Solution
Separate abstraction (the stable hierarchy) from implementation (the interchangeable hierarchy) and connect them through composition.
#Example in C#
public interface IRenderer { void DrawCircle(double x, double y, double r); }
public class SvgRenderer : IRenderer { public void DrawCircle(double x,double y,double r) {/* svg */} }
public class CanvasRenderer : IRenderer { public void DrawCircle(double x,double y,double r) {/* canvas */} }
public abstract class Shape
{
protected readonly IRenderer Renderer;
protected Shape(IRenderer r) => Renderer = r;
public abstract void Draw();
}
public class Circle : Shape
{
private readonly double _x, _y, _r;
public Circle(IRenderer r, double x, double y, double radius) : base(r)
=> (_x, _y, _r) = (x, y, radius);
public override void Draw() => Renderer.DrawCircle(_x, _y, _r);
}When NOT to use it
When you only have one implementation or don't expect new variants.
Tradeoffs
| Pro | Con |
|---|---|
| Avoids combinatorial explosion | Higher initial complexity |
| Allows changing implementation at runtime | More indirection |
#structural #gof