What It Is
The Bridge pattern splits one concept into two independent dimensions so they can vary separately. Shapes and ways of drawing them; messages and ways of sending them.
Without the bridge you get one type per combination — VectorCircle, TextCircle, VectorSquare... — and the list multiplies. With the bridge, one side holds an interface to the other: any shape works with any renderer.
Example: Shapes and Renderers
2 renderers × 2 shapes would be 4 combined types; here it is 2 + 2, connected by one interface field — the bridge.
Any shape, any renderer
The same circle draws itself two ways just by holding a different renderer. Adding a renderer or a shape adds one type, never a whole row of combinations.
Output: Go program and captured output
package main
import "fmt"
// One dimension: HOW to draw.
type Renderer interface {
Draw(name string) string
}
type VectorRenderer struct{}
func (VectorRenderer) Draw(name string) string {
return "drawing " + name + " with smooth vector lines"
}
type TextRenderer struct{}
func (TextRenderer) Draw(name string) string {
return "drawing " + name + " with text characters"
}
// Other dimension: WHAT to draw. The renderer field is the bridge.
type Shape struct {
name string
renderer Renderer
}
func (s Shape) Draw() string {
return s.renderer.Draw(s.name)
}
func main() {
shapes := []Shape{
{name: "circle", renderer: VectorRenderer{}},
{name: "circle", renderer: TextRenderer{}},
{name: "square", renderer: VectorRenderer{}},
}
for _, s := range shapes {
fmt.Println(s.Draw())
}
}
When to Use It
Use it when you notice combination explosion: every new kind on one axis forces new types on the other. Two independent axes — split them.
Skip it when one dimension has exactly one implementation. A bridge to a single shore is just a pier.
Pros and Cons
Pros: the two sides grow independently; combinations come free; each side is testable alone.
Cons: more indirection up front; recognizing the two axes early is genuinely hard — splitting too early creates abstraction nobody needs.