Bridge Pattern separates abstraction from implementation, allowing them to evolve independently. Learn how to apply this pattern to reduce coupling between components and scale your system more easily.

No table of contents available for this article
Bridge Pattern belongs to the Structural Pattern family - design patterns that help organize code structure more effectively.
Core Idea: Bridge separates the abstraction from its concrete implementation. This allows you to modify or upgrade one without affecting the other.
Think of it simply: When you use a remote control (abstraction) to turn on a TV (implementation), you don't need to know how the TV works internally. You can replace the TV with a new model and still use the same remote - that's the essence of Bridge Pattern.
Bridge Pattern is used when:
You want to decouple Abstraction and Implementation for independent extensibility
Both Abstraction and Implementation need to be extended through subclasses
Changes in implementation should not affect the client side
To understand better, let's look at 3 real-world examples:
Imagine developing an RPG game. Characters have 2 attributes:
Race: Human, Elf, Dwarf
Class: Warrior, Mage, Assassin
Using pure inheritance, you'd need:
WarriorHuman, MageHuman, AssassinHuman
WarriorElf, MageElf, AssassinElf
WarriorDwarf, MageDwarf, AssassinDwarf
→ Total: 9 classes! Add one more race or class → create even more classes.
A company has various employee types with different salary calculations:
Junior Developer, Senior Developer, Team Lead
Each position across different departments: Engineering, Marketing, Sales
Using inheritance:
JuniorDeveloperEngineering, SeniorDeveloperEngineering, TeamLeadEngineering
JuniorDeveloperMarketing, SeniorDeveloperMarketing, TeamLeadMarketing
...
→ Number of classes grows exponentially!
You have:
Shapes: Circle, Square
Colors: Red, Blue
With inheritance, you need:
BlueCircle, RedCircle
BlueSquare, RedSquare
Add Yellow color? → Must create YellowCircle, YellowSquare... Add Triangle? → Again create BlueTriangle, RedTriangle, YellowTriangle...
→ Problem: Each time you add a shape or color, the number of classes increases exponentially.
Root Cause: You're trying to extend classes in 2 independent dimensions (shape and color), but OOP inheritance doesn't support this well.
Bridge Pattern shifts from inheritance to composition.
Instead of:
RedSquare inherits from Square and applies red colorBridge Pattern does:
Square has a Color property (composition)
Color can be Red, Blue, Yellow...
→ RedSquare = Square + Color(Red)Benefits:
Add new color? → Just create new color class (Green, Purple...)
Add new shape? → Just create new shape class (Triangle, Pentagon...)
No need to create combination classes!
Bridge Pattern consists of 4 main components:
1. Abstraction (Shape)
Defines high-level interface for clients
Contains reference to Implementation
Example: abstract class Shape with Color property
2. Refined Abstraction (Circle, Square)
Inherits from Abstraction
Provides different variants of abstraction logic
Example: Circle and Square are concrete shapes
3. Implementation (Color)
Defines interface for implementation classes
Doesn't have to match Abstraction interface
Example: Color interface with GetColor() method
4. Concrete Implementation (Red, Blue)
Concrete implementation of Implementation interface
Example: Red returns red color, Blue returns blue color
Relationships:
Client → Abstraction → Implementation
↓ ↓
Refined Abstraction Concrete Implementation1. Reduced Coupling (Loose Coupling)
Abstraction and Implementation are completely independent
Can change implementation at runtime
No recompilation needed when changing either part
2. Reduced Unnecessary Classes
Real Example: Multi-platform image viewer
6 image types: JPG, PNG, GIF, BMP, JPEG, TIFF
3 operating systems: Windows, MacOS, Ubuntu
Without Bridge: 6 × 3 = 18 classes
With Bridge: 6 + 3 = 9 classes
Savings: 50% fewer classes!
3. Cleaner and More Maintainable Code
Each class has clear responsibility
Easy to test components independently
Easy to debug errors
4. Easy Extensibility
Add new abstraction? → Doesn't affect implementation
Add new implementation? → Doesn't affect abstraction
Perfect for agile development!
5. Hides Implementation Details from Client
Client only works with abstraction
Can upgrade internal algorithms without client knowing
Increases security and encapsulation
1. Increased Initial Complexity
Requires more classes and interfaces
Can lead to "over-engineering" for simple applications
Team needs to understand the pattern for maintenance
Advice: Only apply Bridge when you truly need to extend in multiple independent dimensions. For simple classes, regular inheritance is still better.
Apply Bridge when:
✅ You have classes that need extension in 2+ independent dimensions
Examples: Shape + Color, Character + Profession, Device + Remote Control
✅ You want to avoid "class explosion"
Class count grows rapidly with pure inheritance
✅ Need to change implementation at runtime
Example: Switch between different databases (MySQL, PostgreSQL, MongoDB)
✅ Need to share implementation across multiple objects
Save memory when multiple objects use the same implementation
✅ Working with frameworks/libraries that can't be modified
Need to extend functionality without touching original code
Here's a complete implementation for the Shape and Color example:
CODE EXAMPLE:
using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("=== BRIDGE PATTERN DEMO ===\n");
// Create colors (Implementation)
IColor blue = new Blue();
IColor red = new Red();
// Create shapes with corresponding colors
Shape blueSquare = new Square(blue);
Shape redSquare = new Square(red);
Shape blueCircle = new Circle(blue);
Shape redCircle = new Circle(red);
// Display results
Console.WriteLine($"Blue square: {blueSquare.GetColor()}");
Console.WriteLine($"Red square: {redSquare.GetColor()}");
Console.WriteLine($"Blue circle: {blueCircle.GetColor()}");
Console.WriteLine($"Red circle: {redCircle.GetColor()}");
// Demo runtime color change
Console.WriteLine("\n=== RUNTIME COLOR CHANGE ===");
blueSquare.SetColor(red);
Console.WriteLine($"Square after change: {blueSquare.GetColor()}");
}
}
// IMPLEMENTATION INTERFACE
interface IColor
{
string GetColor();
}
// CONCRETE IMPLEMENTATIONS
class Blue : IColor
{
public string GetColor()
{
return "Blue Color";
}
}
class Red : IColor
{
public string GetColor()
{
return "Red Color";
}
}
// ABSTRACTION
abstract class Shape
{
protected IColor color;
protected Shape(IColor color)
{
this.color = color;
}
public string GetColor()
{
return color.GetColor();
}
public void SetColor(IColor newColor)
{
this.color = newColor;
}
}
// REFINED ABSTRACTIONS
class Square : Shape
{
public Square(IColor color) : base(color)
{
}
}
class Circle : Shape
{
public Circle(IColor color) : base(color)
{
}
}Output:
=== BRIDGE PATTERN DEMO ===
Blue square: Blue Color
Red square: Red Color
Blue circle: Blue Color
Red circle: Red Color
=== RUNTIME COLOR CHANGE ===
Square after change: Red ColorImplementation Notes:
Identify Independent Extension Dimensions
Abstraction/Platform, Domain/Infrastructure
Front-end/Back-end, Interface/Implementation
Ensure Implementation Follows Interface
All concrete implementations must implement the same interface
Allows Abstraction to work with any implementation
Use Composition Over Inheritance
Abstraction contains reference to Implementation
Delegates work to implementation object
Client Initialization Done Right
Pass implementation into abstraction's constructor
Then only work with abstraction
Similarities: Both delegate work to another class
Differences:
Bridge: Design remote control separately from TV from the start
Adapter: TV already exists, you need adapter to make old remote work with new TV
Abstract Factory can combine with Bridge to create the right implementation:
CODE EXAMPLE:
interface IColorFactory
{
IColor CreatePrimaryColor();
IColor CreateSecondaryColor();
}
class LightThemeFactory : IColorFactory
{
public IColor CreatePrimaryColor() => new Blue();
public IColor CreateSecondaryColor() => new White();
}
class DarkThemeFactory : IColorFactory
{
public IColor CreatePrimaryColor() => new Red();
public IColor CreateSecondaryColor() => new Black();
}Builder can act as Abstraction, with other Builder classes as Implementation:
CODE EXAMPLE:
class ShapeDirector
{
private IShapeBuilder builder;
public ShapeDirector(IShapeBuilder builder)
{
this.builder = builder;
}
public void Construct()
{
builder.BuildShape();
builder.ApplyColor();
}
}If you found this article helpful, explore more in the Design Patterns Series to enhance your programming skills!
[1] Refactoring.Guru. https://refactoring.guru/design-patterns
[2] Design Patterns for Dummies, Steve Holzner, PhD
[3] Head First Design Patterns, Eric Freeman
[4] Gang of Four Design Patterns 4.0
[5] Dive into Design Patterns