Last Updated: February 23, 2026
The Bridge Design Pattern is a structural pattern that lets you decouple an abstraction from its implementation, allowing the two to vary independently.
It’s particularly useful in situations where:
The Bridge Pattern splits a class into two separate hierarchies:
These two hierarchies are "bridged" via composition (not inheritance) allowing you to mix and match independently.
Let’s walk through a real-world example to see how we can apply the Bridge Pattern to build a system that is both flexible and scalable, without being buried under layers of rigid subclasses.
Imagine you're building a cross-platform graphics library. It supports rendering shapes like circles and rectangles using different rendering approaches:
Now, you need to support:
Circle, Rectangle)VectorRenderer, RasterRenderer)You might start by creating a class hierarchy that looks like this:
Here is what this class hierarchy looks like:
Every red node is a class that fuses two concerns: shape identity and rendering strategy. This is only 2 shapes and 2 renderers. The problem compounds quickly.
Every new combination of shape and rendering method requires a new subclass:
This makes the class hierarchy bloated and rigid.
Each class ties together shape logic and rendering logic. You cannot reuse rendering behavior independently of the shape. They are intertwined in every subclass.
If you want to support a new rendering engine, you must modify or recreate every shape for that renderer.
We need a solution that:
Shape) from its implementation (Renderer)This is exactly where the Bridge Pattern comes in.
The Bridge Design Pattern lets you split a class into two separate hierarchies: one for the abstraction and another for the implementation, so that they can evolve independently.
Two characteristics define the pattern:
Think of a TV remote control and the TV itself. The remote is the abstraction: it has buttons for power, volume, and channel. The TV is the implementation: it contains the circuits that actually change volume, switch channels, and toggle power. You can swap remotes without changing the TV, and you can swap TVs without changing the remote.
A basic remote works with a Samsung TV. The same Samsung TV works with a universal remote that has extra buttons. The remote hierarchy and the TV hierarchy vary independently, connected only by the infrared signal between them. That signal is the bridge.
The structure involves four participants:
Shape)The high-level interface that clients interact with. It defines operations in terms that make sense to the domain (e.g., "draw a shape") and delegates the low-level work to an implementor.
In our shapes example, Shape is the Abstraction. It holds a reference to a Renderer and declares a draw() method. The Shape knows what to draw, the Renderer knows how to draw it.
Circle, Rectangle)A concrete subclass of Abstraction that adds domain-specific state or behavior. It still delegates to the implementor for low-level operations.
In our example, Circle adds a radius field and passes it to the renderer when drawing. Rectangle adds width and height. Neither knows or cares whether the renderer uses vectors or pixels.
Renderer)The interface that defines the low-level operations that concrete implementations must provide. This is the "other side" of the bridge.
In our example, Renderer declares methods like renderCircle(radius) and renderRectangle(width, height). These are primitive rendering operations that different engines implement differently.
VectorRenderer,RasterRenderer)A concrete class that implements the Implementor interface with a specific technology or strategy.
In our example, VectorRenderer outputs vector-based instructions and RasterRenderer outputs pixel-based instructions. Neither knows whether it is being called by a Circle or a Rectangle.
Here is the Bridge workflow, step by step:
The client (or a factory) instantiates a specific implementation, for example VectorRenderer. This object knows how to perform low-level rendering operations using vector graphics.
The client creates a Circle and passes the VectorRenderer into its constructor. The Circle stores this reference internally. It does not know or care what kind of renderer it received.
The client calls circle.draw(). This is a domain-level operation: "draw this shape." The client does not think about rendering engines.
Inside draw(), the Circle calls renderer.renderCircle(radius). It translates the high-level operation ("draw me") into a low-level operation ("render a circle with this radius") and delegates to the implementor.
The VectorRenderer runs its own rendering logic, producing vector output. It has no idea it was called by a Circle. It just received a radius and did its job.
Let us now implement the Bridge pattern to decouple our Shape abstraction from the Renderer implementation. This allows us to mix and match shapes and rendering engines freely, without subclass explosion.
Renderer)This interface declares rendering operations for shapes. Concrete implementations will define how to render shapes using a particular technique (vector, raster, etc.).
These classes provide the actual rendering logic for each engine.
Shape)This class holds a reference to the renderer and declares a general draw() method. Each concrete shape will implement draw() by delegating to the renderer.
Each shape delegates rendering to the renderer passed into it. The shape knows its own properties (radius, width, height), and the renderer knows how to draw primitives. Neither knows about the other's internals.
Now we can freely combine shapes and rendering strategies at runtime, without creating a new class for each combination.
To make sure Bridge clicks beyond the shapes example, let us build a completely different system: remote controls and electronic devices. The abstraction is the remote (basic remote, advanced remote), and the implementation is the device (TV, radio).
A basic remote can toggle power and adjust volume. An advanced remote adds a mute button. Both remotes work with any device.
The Device interface is the Implementor. TV and Radio are ConcreteImplementors. Remote is the Abstraction. BasicRemote and AdvancedRemote are RefinedAbstractions. The bridge is the reference from Remote to Device.
Notice how the same TV object is used with both the basic remote and the advanced remote. The remote hierarchy and the device hierarchy vary independently. Adding a Speaker device means writing one new class. Adding a VoiceRemote means writing one new class. Neither side knows about the other.