Connect with me
Structural Design Pattern: Bridge
The Bridge Design Pattern is a structural pattern that decouples an abstraction from its implementation so that both can vary independently. It is commonly used when a class grows in two orthogonal dimensions and inheritance alone cannot prevent the explosion of subclasses.

This article is part of a series exploring design patterns using the Java programming language. The goal of this series is to help readers develop a solid understanding of design patterns while also sharing real-world examples from actual codebases that make use of these patterns.
In this article, we’ll be discussing the Bridge pattern.
Symbols for better navigation:
🤔 Hypothetical Scenario/Imagination
⚠️ Warning
👉 Point to note
📝 Common Understanding
Bridge Pattern
The Bridge Pattern is a structural pattern that decouples an abstraction from its implementation so that both can vary independently.
The Bridge pattern is commonly used when a class grows to have different features acting as independent dimensions, which can often lead to the class explosion problem via inheritance.
🤔 Imagine this: you are creating a graphics application that draws shapes. You start with a base class Shape and subclasses like Square and Circle.
Now, business requirements change, and you need to incorporate colors: Red and Blue. If you rely solely on inheritance, you end up extending your classes like this:
RedSquareBlueSquareRedCircleBlueCircle
This doesn’t look too bad yet. But what happens if you add a Triangle?
You now need a RedTriangle and BlueTriangle.
What if you add a Green color? You now need a GreenSquare, GreenCircle, and GreenTriangle!
With 3 Shapes and 3 Colors, you need 3 × 3 = 9 classes.
In reality, the Shape and the Color are two different dimensions that should grow independently of each other.
This is what the Bridge Design pattern helps us achieve. It bridges the Abstraction (Shape) to the Implementation (Color).
In coding terms, this means that even if the Color interface grows to accommodate a variety of new colors (Implementation), the Shape (Abstraction) will not be forced to change or extend its hierarchy unnecessarily.
Recipe to cook the Bridge Pattern for objects
Structuring objects via the Bridge Design Pattern can be done using the following steps:
- Identify the orthogonal dimensions: Look for concepts in your domain that should vary independently (e.g., Shape vs. Color, Payment Method vs. Payment Gateway).
- Identify the Implementation (Low Level): Define an interface for the operational details (e.g., the
Colorinterface). - Identify the Abstraction (High Level): Define the high-level control class (e.g., the
Shapeabstract class). - Bridge them: Modify the Abstraction to contain a reference (field) to the Implementation type.
- Constructor Injection: Modify the Abstraction’s constructor to accept the specific Implementation object at runtime.
Case-Study
Let’s look at a more business-centric scenario. Suppose we are building a video streaming platform.
We have two dimensions of variation:
- Video Quality: 4K, 1080p.
- Video Platform: YouTube, Netflix.
Without the Bridge pattern, if we try to implement this using inheritance, we will end up with classes like YouTube4KProcess, YouTube1080pProcess, Netflix4KProcess, etc.
Let’s apply the Bridge pattern to decouple the Video (Abstraction) from the Processor (Implementation).
First, we define the implementation interface. This deals with the low-level logic of rendering the video.
interface VideoProcessor {
void process(String text);
}
class YouTubeProcessor implements VideoProcessor {
@Override
public void process(String text) {
System.out.println("YouTube Processing: " + text);
}
}
class NetflixProcessor implements VideoProcessor {
@Override
public void process(String text) {
System.out.println("Netflix Processing: " + text);
}
}Now, we define the abstraction. This is the high-level control that uses the processor. Note that it holds a reference to the VideoProcessor.
abstract class Video {
protected VideoProcessor processor;
public Video(VideoProcessor processor) {
this.processor = processor; // reference to implementation
}
abstract void play(String videoFile);
}Now we can create refined abstractions that vary independently of the platform.
class UHDRenderer extends Video {
// 4K
public UHDRenderer(VideoProcessor processor) {
super(processor);
}
@Override
public void play(String videoFile) {
processor.process(videoFile + " [Rendered in 4K UHD]");
}
}
class HDRenderer extends Video {
// 1080p
public HDRenderer(VideoProcessor processor) {
super(processor);
}
@Override
public void play(String videoFile) {
processor.process(videoFile + " [Rendered in 1080p HD]");
}
}By using the Bridge pattern, we can now mix and match any Video quality with any Platform without creating new classes.
public class BridgeDemo {
public static void main(String[] args) {
Video youtubeVideo = new UHDRenderer(new YouTubeProcessor());
youtubeVideo.play("DesignPatterns.mp4");
Video netflixVideo = new HDRenderer(new NetflixProcessor());
netflixVideo.play("Movie.mkv");
}
}👉 Notice the flexibility: If we want to add a new platform (e.g., TwitchProcessor), we don’t need to touch the UHDRenderer or HDRenderer classes. The abstraction and implementation can evolve separately.
Real-World Use Case of Bridge
A classic real-world example of the Bridge pattern in the Java ecosystem is JDBC (Java Database Connectivity).
The JDBC API acts as the Abstraction. It provides a standard set of interfaces (Connection, Statement, ResultSet) that the application developer uses.
The Implementation is provided by the database vendors (MySQL, PostgreSQL, Oracle) via their specific Drivers.
When you write code to query a database, you write it against the JDBC abstraction:
Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
stmt.executeQuery("SELECT * FROM USERS");Behind the scenes, the DriverManager bridges your request to the concrete implementation (the Driver) loaded at runtime.
- Abstraction: The application logic using generic JDBC interfaces.
- Implementation:
com.mysql.cj.jdbc.Driverororg.postgresql.Driver.
This allows the Java language to add new database features without caring about how MySQL stores data, and it allows MySQL to update its internal storage engine without breaking your Java code.
Subscribe to my newsletter today!



