Bridge Pattern(橋接模式)

是什麼?

Bridge Pattern 將抽象部分與它的實作部分分離,使它們都可以獨立地變化。它用組合取代繼承,避免類別數量因為多維度變化而爆炸性增長。

ℹ️GoF 分類

Bridge 屬於結構型模式(Structural Pattern),重點在於如何組合類別和物件以形成更大的結構。

什麼時候用?

用以下 if/then 條件判斷:

什麼時候不該用?

⚠️過度設計警告

如果你的類別只有單一維度的變化(例如只有不同的資料庫),用 Strategy Pattern 或簡單的介面抽換就夠了。Bridge 是為了解決多維度同時變化的問題,只有一個維度時不需要這麼複雜的結構。

執行流程

1

辨識多維度變化

發現類別有兩個以上獨立變化的維度

2

分離抽象與實作

將一個維度定義為抽象層,另一個定義為實作層

3

定義實作介面

宣告實作層的方法簽名(Implementor)

4

建立橋接

抽象層持有實作層的參考,透過組合連接

5

獨立擴充

兩邊各自新增子類別,互不影響

流程解讀:Bridge 的核心是「把一個類別的兩個變化維度拆開」。先辨識哪些是抽象層的變化(如形狀:圓形、方形)、哪些是實作層的變化(如渲染方式:HTML、SVG)。然後用組合而非繼承連接兩者 — 抽象層持有 Implementor 的參考。這讓類別總數從「乘法」(M x N)降為「加法」(M + N),每個維度都能獨立擴充。

程式碼範例

C# 版本

csharp
// 實作層介面(Implementor)
public interface IRenderer
{
    string RenderShape(string shapeName, double area);
}
 
// 具體實作
public class HtmlRenderer : IRenderer
{
    public string RenderShape(string shapeName, double area)
        => $"<div class='{shapeName}'>Area: {area:F1}</div>";
}
 
public class SvgRenderer : IRenderer
{
    public string RenderShape(string shapeName, double area)
        => $"<svg><text>{shapeName} - Area: {area:F1}</text></svg>";
}
 
// 抽象層
public abstract class Shape
{
    protected IRenderer Renderer;
 
    protected Shape(IRenderer renderer) { Renderer = renderer; }
 
    public abstract string Draw();
}
 
// 擴充抽象
public class Circle : Shape
{
    private readonly double _radius;
 
    public Circle(double radius, IRenderer renderer) : base(renderer)
    {
        _radius = radius;
    }
 
    public override string Draw()
        => Renderer.RenderShape("circle", Math.PI * _radius * _radius);
}
 
public class Square : Shape
{
    private readonly double _side;
 
    public Square(double side, IRenderer renderer) : base(renderer)
    {
        _side = side;
    }
 
    public override string Draw()
        => Renderer.RenderShape("square", _side * _side);
}
 
// 使用:形狀和渲染方式可以自由組合
var htmlCircle = new Circle(5, new HtmlRenderer());
var svgSquare = new Square(4, new SvgRenderer());
Console.WriteLine(htmlCircle.Draw());  // <div class='circle'>Area: 78.5</div>
Console.WriteLine(svgSquare.Draw());   // <svg><text>square - Area: 16.0</text></svg>

TypeScript 版本

typescript
// 實作層介面
interface Renderer {
  renderShape(shapeName: string, area: number): string;
}
 
// 具體實作
class HtmlRenderer implements Renderer {
  renderShape(shapeName: string, area: number): string {
    return `<div class='${shapeName}'>Area: ${area.toFixed(1)}</div>`;
  }
}
 
class SvgRenderer implements Renderer {
  renderShape(shapeName: string, area: number): string {
    return `<svg><text>${shapeName} - Area: ${area.toFixed(1)}</text></svg>`;
  }
}
 
// 抽象層
abstract class Shape {
  constructor(protected renderer: Renderer) {}
  abstract draw(): string;
}
 
// 擴充抽象
class Circle extends Shape {
  constructor(private radius: number, renderer: Renderer) {
    super(renderer);
  }
 
  draw(): string {
    return this.renderer.renderShape("circle", Math.PI * this.radius ** 2);
  }
}
 
class Square extends Shape {
  constructor(private side: number, renderer: Renderer) {
    super(renderer);
  }
 
  draw(): string {
    return this.renderer.renderShape("square", this.side ** 2);
  }
}
 
// 使用
const htmlCircle = new Circle(5, new HtmlRenderer());
const svgSquare = new Square(4, new SvgRenderer());
console.log(htmlCircle.draw());
console.log(svgSquare.draw());

Python 版本

python
from abc import ABC, abstractmethod
import math
 
# 實作層介面
class Renderer(ABC):
    @abstractmethod
    def render_shape(self, shape_name: str, area: float) -> str:
        pass
 
# 具體實作
class HtmlRenderer(Renderer):
    def render_shape(self, shape_name: str, area: float) -> str:
        return f"<div class='{shape_name}'>Area: {area:.1f}</div>"
 
class SvgRenderer(Renderer):
    def render_shape(self, shape_name: str, area: float) -> str:
        return f"<svg><text>{shape_name} - Area: {area:.1f}</text></svg>"
 
# 抽象層
class Shape(ABC):
    def __init__(self, renderer: Renderer):
        self._renderer = renderer
 
    @abstractmethod
    def draw(self) -> str:
        pass
 
# 擴充抽象
class Circle(Shape):
    def __init__(self, radius: float, renderer: Renderer):
        super().__init__(renderer)
        self._radius = radius
 
    def draw(self) -> str:
        return self._renderer.render_shape("circle", math.pi * self._radius ** 2)
 
class Square(Shape):
    def __init__(self, side: float, renderer: Renderer):
        super().__init__(renderer)
        self._side = side
 
    def draw(self) -> str:
        return self._renderer.render_shape("square", self._side ** 2)
 
# 使用
html_circle = Circle(5, HtmlRenderer())
svg_square = Square(4, SvgRenderer())
print(html_circle.draw())
print(svg_square.draw())

Java 版本

java
// 實作層介面
public interface Renderer {
    String renderShape(String shapeName, double area);
}
 
// 具體實作
public class HtmlRenderer implements Renderer {
    public String renderShape(String shapeName, double area) {
        return String.format("<div class='%s'>Area: %.1f</div>", shapeName, area);
    }
}
 
public class SvgRenderer implements Renderer {
    public String renderShape(String shapeName, double area) {
        return String.format("<svg><text>%s - Area: %.1f</text></svg>", shapeName, area);
    }
}
 
// 抽象層
public abstract class Shape {
    protected Renderer renderer;
 
    public Shape(Renderer renderer) { this.renderer = renderer; }
 
    public abstract String draw();
}
 
// 擴充抽象
public class Circle extends Shape {
    private double radius;
 
    public Circle(double radius, Renderer renderer) {
        super(renderer);
        this.radius = radius;
    }
 
    public String draw() {
        return renderer.renderShape("circle", Math.PI * radius * radius);
    }
}
 
public class Square extends Shape {
    private double side;
 
    public Square(double side, Renderer renderer) {
        super(renderer);
        this.side = side;
    }
 
    public String draw() {
        return renderer.renderShape("square", side * side);
    }
}
 
// 使用
Shape htmlCircle = new Circle(5, new HtmlRenderer());
Shape svgSquare = new Square(4, new SvgRenderer());
System.out.println(htmlCircle.draw());
System.out.println(svgSquare.draw());

結構圖

Client
uses
Abstraction (Shape)
delegates to (bridge)
RefinedAbstraction (Circle/Square)
extends
Implementor (Renderer)
ConcreteImplementor (Html/Svg)

結構解讀:圖中有兩條獨立的繼承鏈:左邊是抽象層(Shape → Circle/Square),右邊是實作層(Renderer → Html/Svg)。中間的「bridge」就是 Abstraction 持有 Implementor 的參考(組合關係)。新增 Triangle 只需在左邊加一個類別,新增 CanvasRenderer 只需在右邊加一個類別,兩邊互不干擾。

實戰補充

💡資深開發者經驗

Bridge 最常見的實戰場景是跨平台渲染。例如一個繪圖框架需要支援 DirectX、OpenGL、Vulkan 三種渲染引擎,同時有 2D 和 3D 兩種繪圖模式。如果用繼承會產生 6 個子類別,每加一個維度就倍增。用 Bridge 把「繪圖模式(抽象)」和「渲染引擎(實作)」分開,各自擴充互不影響。在企業應用中,「資料存取層 x 資料庫類型」也是經典的 Bridge 場景。

理解測驗

🤔 Bridge Pattern 主要解決什麼問題?

🤔 Bridge Pattern 中的「橋」指的是什麼?

🤔 如果有 3 種形狀和 4 種渲染方式,用繼承需要幾個類別?用 Bridge 呢?

面試常見問題

Q: Bridge 和 Strategy 看起來很像(都是持有介面參考),怎麼區分?

A: 關鍵差異在於「維度數量」和「意圖」。Strategy 處理單一維度的變化(一個 Context 替換一種演算法)。Bridge 處理兩個維度的交叉變化(抽象層和實作層各自獨立擴充)。判斷方式:如果你只需要替換一種行為 → Strategy;如果你面對兩個獨立的變化軸 → Bridge。

Q: Bridge 和 Adapter 都是「把兩個東西連起來」,差在哪?

A: 時機和意圖不同。Bridge 在設計階段就預先分離兩個維度,是主動的架構設計。Adapter 是事後補救,連接兩個已經存在但介面不合的東西。口訣:「Bridge 是預防,Adapter 是治療」。

相關模式

| 模式 | 關係 | |------|------| | Strategy | 結構相似但維度不同 — Strategy 處理單一維度行為替換,Bridge 處理雙維度交叉變化 | | Adapter | Bridge 是設計階段的預先分離,Adapter 是事後的介面補救 | | Abstract Factory | 可搭配使用 — Abstract Factory 負責建立 Bridge 中正確的 Implementor 實例 | | Decorator | 都用組合擴充功能,但 Bridge 分離兩個維度,Decorator 在單一維度疊加功能 |

重點整理

💡一句話記住

Bridge Pattern = 雙維度分離:抽象和實作各走各的路,用橋連接。 口訣:「乘法變加法,組合勝繼承」

| 概念 | 說明 | |------|------| | Abstraction(抽象層) | 定義高層操作,持有 Implementor 的參考 | | RefinedAbstraction | 擴充抽象層的具體子類別 | | Implementor(實作層) | 定義實作層的介面 | | ConcreteImplementor | 實作層的具體實作 | | 核心好處 | 兩個維度獨立變化,類別數量從乘法變加法 | | 代價 | 增加了系統的抽象層次,簡單場景可能過度設計 |

你可能也想看

Adapter PatternComposite Pattern

按 ← → 鍵切換課程