June 17, 2026 in patterns5 minutes
A intermediate-level guide to Bridge: before-and-after java code and diagrams for a CS student.
Imagine you are building a graphics engine. Initially, you only have two shapes: Circles and Squares. You implement them using inheritance to handle different colors, creating RedCircle and BlueCircle.
The problem arises when your requirements grow. If you add a third color, Green, you must create GreenCircle and GreenSquare. If you then decide to support a new shape, Triangle, the explosion begins: you need RedTriangle, BlueTriangle, and GreenTriangle.
As you can see in this diagram, adding just one new dimension (like a new color or a new shape type) forces you to multiply your existing classes. This is a “class explosion” caused by multi-dimensional inheritance.
classDiagram
class Shape {
<<abstract>>
+draw() void
}
class RedCircle {
+draw() void
}
class BlueCircle {
+draw() void
}
class RedSquare {
+draw() void
}
class BlueSquare {
+draw() void
}
Shape <|-- RedCircle
Shape <|-- BlueCircle
Shape <|-- RedSquare
Shape <|-- BlueSquare
// Problem: Class explosion due to multi-dimensional inheritance.
// To add a new color OR a new shape, we must create multiple new subclasses.
abstract class Shape {
abstract void draw();
}
class RedCircle extends Shape {
@Override
void draw() {
System.out.println("Drawing a red circle");
}
}
class BlueCircle extends Shape {
@Override
void draw() {
System.out.println("Drawing a blue circle");
}
}
class RedSquare extends Shape {
@Override
void draw() {
System.out.println("Drawing a red square");
}
}
class BlueSquare extends Shape {
@Override
void draw() {
System.out.println("Drawing a blue square");
}
}The Bridge pattern solves this by replacing inheritance with composition. Instead of trying to define a class that is both a “Circle” and “Red,” we split the problem into two separate hierarchies:
Shape itself).Color).Instead of inheriting color properties, a Shape holds a private reference to a Color object. When the Shape needs to perform an action involving color, it delegates that task to its color reference. This is known as Delegation.
By using this “bridge” between the two hierarchies, adding a new shape only requires one new class, and adding a new color only requires one new class. The growth becomes linear rather than exponential.
classDiagram
class Shape {
<<abstract>>
#color: Color
+draw() void
}
class Circle {
+draw() void
}
class Square {
+draw() void
}
class Color {
<<interface>>
+apply() void
}
class RedColor {
+apply() void
}
class BlueColor {
+apply() void
}
Shape <|-- Circle
Shape <|-- Square
Shape o-- Color : color
Color <|.. RedColor
Color <|.. BlueColor
To understand how these objects interact at runtime, look at how a Circle uses its delegated color object to fulfill a request.
sequenceDiagram
participant S as Circle
participant C as RedColor
S->>C: apply()
Note over C: Prints "red"
C-->>S: done
// Solution: Bridge pattern.
// We separate the Abstraction (Shape) from its Implementation (Color).
interface Color {
void apply();
}
class RedColor implements Color {
@Override
public void apply() {
System.out.println("red");
}
}
class BlueColor implements Color {
@Override
public void apply() {
System.out.println("blue");
}
}
abstract class Shape {
protected final Color color;
protected Shape(Color color) {
this.color = color;
}
abstract void draw();
}
class Circle extends Shape {
Circle(Color color) { super(color); }
@Override
void draw() {
System.out.print("Drawing a ");
color.apply();
System.out.println(" circle");
}
}
class Square extends Shape {
Square(Color color) { super(color); }
@Override
void draw() {
System.out.print("Drawing a ");
color.apply();
System.out.println(" square");
}
}To implement this pattern successfully, you need to understand the four distinct roles:
Shape). It maintains a reference to an object of type Implementor.Circle and Square).Color). This interface is separate from the abstraction so they can vary independently.RedColor and BlueColor).While both use composition to delegate work, they serve different purposes. Bridge is a structural pattern used upfront during design to prevent a permanent hierarchy explosion. It separates two dimensions of a class (what it does vs. how it does the details). Strategy is a behavioral pattern typically used at runtime to swap out algorithms for a specific task.
The Bridge pattern is an investment in future flexibility.
Use it when:
Shape and Color, or RemoteControl and Device) that are likely to grow independently.Avoid it when:
Using the Bridge to compose shape and color independently
Shape circle = new Circle(new RedColor());
circle.draw(); // Drawing a red circle