Bridge Pattern¶
Category: Structural Pattern
Overview¶
Decouple an abstraction from its implementation so that the two can vary independently. This pattern uses composition over inheritance to separate the interface from the implementation, allowing both to be extended independently without affecting each other.
Usage Guidelines¶
Use when:
- Want to avoid permanent binding between abstraction and implementation
- Both abstraction and implementation should be extended independently
- Need to switch implementations at runtime
- Multiple implementations of abstraction exist
Avoid when:
- Only one implementation exists
- Implementation doesn't vary
- Simple inheritance suffices
- Extra indirection is unacceptable for performance
Implementation¶
from __future__ import annotations
from abc import ABC, abstractmethod
# Implementation Interface
class Renderer(ABC):
"""Abstract implementation interface for rendering."""
@abstractmethod
def render_circle(self, radius: float) -> str:
"""Render a circle."""
pass
@abstractmethod
def render_square(self, side: float) -> str:
"""Render a square."""
pass
# Concrete Implementations
class VectorRenderer(Renderer):
"""Concrete implementation for vector rendering."""
def render_circle(self, radius: float) -> str:
"""Render circle as vector."""
return f"Drawing circle with radius {radius} as vector"
def render_square(self, side: float) -> str:
"""Render square as vector."""
return f"Drawing square with side {side} as vector"
class RasterRenderer(Renderer):
"""Concrete implementation for raster rendering."""
def render_circle(self, radius: float) -> str:
"""Render circle as raster."""
return f"Drawing circle with radius {radius} as pixels"
def render_square(self, side: float) -> str:
"""Render square as raster."""
return f"Drawing square with side {side} as pixels"
# Abstraction
class Shape(ABC):
"""Abstract shape class using bridge to renderer."""
def __init__(self, renderer: Renderer) -> None:
"""Initialize shape with a renderer."""
self.renderer = renderer
@abstractmethod
def draw(self) -> str:
"""Draw the shape."""
pass
@abstractmethod
def resize(self, factor: float) -> None:
"""Resize the shape."""
pass
# Refined Abstractions
class Circle(Shape):
"""Concrete circle shape."""
def __init__(self, renderer: Renderer, radius: float = 5.0) -> None:
"""Initialize circle."""
super().__init__(renderer)
self.radius = radius
def draw(self) -> str:
"""Draw circle using renderer."""
return self.renderer.render_circle(self.radius)
def resize(self, factor: float) -> None:
"""Resize circle."""
self.radius *= factor
class Square(Shape):
"""Concrete square shape."""
def __init__(self, renderer: Renderer, side: float = 5.0) -> None:
"""Initialize square."""
super().__init__(renderer)
self.side = side
def draw(self) -> str:
"""Draw square using renderer."""
return self.renderer.render_square(self.side)
def resize(self, factor: float) -> None:
"""Resize square."""
self.side *= factor
Usage¶
# Create shapes with different renderers
circle_vector = Circle(VectorRenderer(), 5.0)
print(circle_vector.draw()) # Drawing circle with radius 5.0 as vector
circle_raster = Circle(RasterRenderer(), 5.0)
print(circle_raster.draw()) # Drawing circle with radius 5.0 as pixels
square_vector = Square(VectorRenderer(), 10.0)
print(square_vector.draw()) # Drawing square with side 10.0 as vector
# Resize and redraw
circle_vector.resize(2.0)
print(circle_vector.draw()) # Drawing circle with radius 10.0 as vector
Trade-offs¶
Benefits:
- Abstraction and implementation can vary independently
- Implementation can be selected or switched at runtime
- Isolates platform-specific code
- New abstractions and implementations without changing existing code (Open/Closed Principle)
Drawbacks:
- Adds complexity with additional abstractions
- Extra layer of indirection
- Can be hard to design proper abstraction/implementation split
- Too complex for simple scenarios
Real-World Examples¶
- GUI frameworks separating GUI from platform-specific rendering
- Database drivers with abstract operations from specific implementations
- Graphics systems separating shapes from rendering methods
- Device drivers separating operations from hardware implementations
Related Patterns¶
- Abstract Factory
- Adapter
- State
API Reference¶
design_patterns.structural.bridge
¶
Bridge Pattern Module
The Bridge pattern decouples an abstraction from its implementation so that the two can vary independently. It uses composition over inheritance to separate the interface from the implementation, allowing both to be extended independently.
Example
Drawing shapes with different rendering implementations:
AdvancedRemoteControl
¶
Bases: RemoteControl
Extended remote control with additional features.
Source code in src/design_patterns/structural/bridge.py
Circle
¶
Bases: Shape
Concrete circle shape.
Source code in src/design_patterns/structural/bridge.py
Device
¶
Bases: ABC
Abstract device interface.
Source code in src/design_patterns/structural/bridge.py
Radio
¶
Bases: Device
Concrete radio device.
Source code in src/design_patterns/structural/bridge.py
RasterRenderer
¶
Bases: Renderer
Concrete implementation for raster rendering.
Source code in src/design_patterns/structural/bridge.py
render_circle(radius)
¶
Render circle as raster.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
radius
|
float
|
Circle radius. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Raster rendering result. |
render_square(side)
¶
Render square as raster.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
side
|
float
|
Square side length. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Raster rendering result. |
RemoteControl
¶
Abstraction for remote control using bridge to device.
Source code in src/design_patterns/structural/bridge.py
__init__(device)
¶
Initialize remote with a device.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
device
|
Device
|
The device to control. |
required |
toggle_power()
¶
Toggle device power.
Returns:
| Type | Description |
|---|---|
str
|
Status message. |
Source code in src/design_patterns/structural/bridge.py
volume_down()
¶
Decrease volume.
Returns:
| Type | Description |
|---|---|
str
|
Status message. |
volume_up()
¶
Increase volume.
Returns:
| Type | Description |
|---|---|
str
|
Status message. |
Renderer
¶
Bases: ABC
Abstract implementation interface for rendering.
Source code in src/design_patterns/structural/bridge.py
render_circle(radius)
abstractmethod
¶
Render a circle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
radius
|
float
|
Circle radius. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Rendering result. |
render_square(side)
abstractmethod
¶
Render a square.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
side
|
float
|
Square side length. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Rendering result. |
Shape
¶
Bases: ABC
Abstract shape class using bridge to renderer.
Source code in src/design_patterns/structural/bridge.py
Square
¶
Bases: Shape
Concrete square shape.
Source code in src/design_patterns/structural/bridge.py
TV
¶
Bases: Device
Concrete TV device.
Source code in src/design_patterns/structural/bridge.py
VectorRenderer
¶
Bases: Renderer
Concrete implementation for vector rendering.
Source code in src/design_patterns/structural/bridge.py
render_circle(radius)
¶
Render circle as vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
radius
|
float
|
Circle radius. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Vector rendering result. |
render_square(side)
¶
Render square as vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
side
|
float
|
Square side length. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Vector rendering result. |