python设计模式–桥接模式(Bridge Pattern)

Python 设计模式——桥接模式(Bridge Pattern)详解

本文面向零基础新手,从“类爆炸”问题到抽象与实现分离,从简单示例到多维度变化,全方位讲解桥接模式。


一、什么是桥接模式?

1.1 通俗理解

桥接模式是一种结构型设计模式,核心思想是:

把“抽象”(高层逻辑、业务概念)和“实现”(底层具体做法)拆开,让它们可以独立变化;通过“组合”把抽象和实现连在一起,而不是用继承把各种组合都写成子类,从而避免“类爆炸”。

可以这样类比:

  • 不用桥接:你要做“红色圆形、蓝色圆形、红色方形、蓝色方形……”每种组合写一个类(RedCircle、BlueCircle、RedSquare、BlueSquare…),再加绿色就再乘一批类,类数量 = 形状数 × 颜色数,爆炸式增长。
  • 用桥接形状是一类东西(抽象),颜色/绘制方式是另一类东西(实现)。一个“形状”对象持有一个“颜色/绘制”对象,画的时候让颜色去画。这样形状类只有圆形、方形…,颜色类只有红、蓝…,类数量 = 形状数 + 颜色数,新增颜色或形状都只加一个类,不互相乘。

所以,桥接模式解决的是:当有两个(或更多)会独立变化的维度时,用“组合”代替“继承枚举所有组合”,避免类爆炸、便于扩展。

1.2 为什么需要桥接?——类爆炸问题

假设你要做“形状 + 颜色”:形状有圆形、方形,颜色有红、蓝。用继承硬拼会怎样?

# 糟糕做法:每个组合一个类
class RedCircle:
    def draw(self):
        print("画红色圆形")

class BlueCircle:
    def draw(self):
        print("画蓝色圆形")

class RedSquare:
    def draw(self):
        print("画红色方形")

class BlueSquare:
    def draw(self):
        print("画蓝色方形")

再加一个“绿色”、一个“三角形”,就要再写 GreenCircle、GreenSquare、GreenTriangle、RedTriangle、BlueTriangle……类数量 = 形状 × 颜色,难以维护。

桥接的做法

  • “形状”是一维(抽象):圆形、方形…
  • “颜色/绘制实现”是另一维(实现):红、蓝…
  • 形状不继承颜色,而是持有一个“颜色/绘制”对象,画的时候委托给它。
    这样加新形状只加形状子类,加新颜色只加颜色子类,两维独立变化

1.3 适用场景(什么时候用?)

场景 说明
多个维度独立变化 如形状×颜色、设备×遥控方式、消息×发送渠道,不想用继承枚举所有组合。
抽象和实现都要能独立扩展 希望加新“类型”时不影响另一维的类。
避免永久绑定 运行时可以换“实现”(如换一种绘制方式、换一种发送方式)。

简单记忆:有两个(或更多)会独立变化的维度,且不想写“维度1 × 维度2”那么多子类时,用桥接。


二、桥接模式的结构(四个角色)

角色 说明
抽象(Abstraction) 高层概念,持有“实现”的引用,把部分工作委托给实现。如“形状”,持有“颜色/绘制”。
refined 抽象(RefinedAbstraction) 对抽象的扩展或具体化。如“圆形”“方形”。
实现者(Implementor) 底层实现的接口,供抽象调用。如“颜色”或“绘制接口”。
具体实现(ConcreteImplementor) 实现者的具体类。如“红色”“蓝色”。

关系可以理解为:

  • 抽象 不直接干底层活,而是通过组合持有一个 实现者,需要时调用实现者的方法。
  • refined 抽象 是抽象的子类,代表一种具体“类型”(如圆形);具体实现 是实现者的子类,代表一种具体“实现”(如红色)。
  • 这样“圆形 + 红色” = 一个 RefinedAbstraction(圆形)里装一个 ConcreteImplementor(红色),不需要 RedCircle 这个类。

三、示例一:形状与颜色(最经典)

需求:形状有圆形、方形;颜色有红、蓝。用桥接让形状和颜色独立变化,任意组合。

3.1 实现者接口 + 具体实现(颜色/绘制)

“颜色”在这里就是“实现”这一维:负责“怎么画”(或提供颜色信息)。

from abc import ABC, abstractmethod

class Color(ABC):
    """实现者接口:颜色(或绘制实现)"""
    @abstractmethod
    def fill(self) -> str:
        """返回填充颜色名,或实际做绘制"""
        pass

class Red(Color):
    def fill(self) -> str:
        return "红色"

class Blue(Color):
    def fill(self) -> str:
        return "蓝色"

3.2 抽象 + 精化抽象(形状)

“形状”是抽象:持有“颜色”,画的时候用颜色的 fill()

class Shape(ABC):
    """抽象:形状,持有颜色(实现)"""
    def __init__(self, color: Color):
        self._color = color

    @abstractmethod
    def draw(self):
        pass

class Circle(Shape):
    """精化抽象:圆形"""
    def draw(self):
        print(f"画一个{self._color.fill()}圆形")

class Square(Shape):
    """精化抽象:方形"""
    def draw(self):
        print(f"画一个{self._color.fill()}方形")

3.3 使用:任意组合,无新类

red_circle = Circle(Red())
blue_circle = Circle(Blue())
red_square = Square(Red())
blue_square = Square(Blue())

red_circle.draw()   # 画一个红色圆形
blue_square.draw()  # 画一个蓝色方形

要点:要“红色圆形”只需 Circle(Red()),不需要 RedCircle 类;加绿色只需加 Green(Color),加三角形只需加 Triangle(Shape),两维独立扩展。


四、示例二:遥控器与设备(抽象 = 遥控,实现 = 设备)

需求:遥控器有普通遥控、高级遥控;设备有电视、音响。遥控器要控制“开/关”等,但具体动作由设备执行。用桥接:抽象 = 遥控器,实现 = 设备接口。

4.1 实现者:设备接口 + 具体设备

class Device(ABC):
    """实现者:设备"""
    @abstractmethod
    def turn_on(self):
        pass

    @abstractmethod
    def turn_off(self):
        pass

class TV(Device):
    def turn_on(self):
        print("电视开机")

    def turn_off(self):
        print("电视关机")

class Radio(Device):
    def turn_on(self):
        print("音响开机")

    def turn_off(self):
        print("音响关机")

4.2 抽象:遥控器(持有设备)

class Remote(ABC):
    """抽象:遥控器,持有设备"""
    def __init__(self, device: Device):
        self._device = device

    def power(self):
        """开/关切换:委托给设备"""
        # 这里简化:只做一次开或关,实际可加状态
        self._device.turn_on()

class BasicRemote(Remote):
    """普通遥控"""
    def power(self):
        print("[普通遥控] 按下电源键")
        self._device.turn_on()

class AdvancedRemote(Remote):
    """高级遥控:多一个静音"""
    def mute(self):
        print("[高级遥控] 静音")
        # 假设设备有 mute,这里仅示意

4.3 使用

tv = TV()
basic = BasicRemote(tv)
basic.power()  # [普通遥控] 按下电源键 n 电视开机

radio = Radio()
adv = AdvancedRemote(radio)
adv.power()   # 音响开机

遥控类型和设备类型可以任意组合,不需要 TVBasicRemote、TVAdvancedRemote、RadioBasicRemote… 那么多类。


五、示例三:消息与发送渠道(抽象 = 消息类型,实现 = 发送方式)

需求:消息有普通消息、紧急消息;发送方式有短信、邮件。用桥接:抽象 = 消息(持有“发送方式”),实现 = 发送渠道。

5.1 实现者:发送渠道

class Sender(ABC):
    @abstractmethod
    def send(self, content: str, to: str):
        pass

class SmsSender(Sender):
    def send(self, content: str, to: str):
        print(f"[短信] 发给 {to}: {content}")

class EmailSender(Sender):
    def send(self, content: str, to: str):
        print(f"[邮件] 发给 {to}: {content}")

5.2 抽象:消息类型(持有发送渠道)

class Message(ABC):
    def __init__(self, sender: Sender):
        self._sender = sender

    @abstractmethod
    def notify(self, to: str, content: str):
        pass

class NormalMessage(Message):
    def notify(self, to: str, content: str):
        self._sender.send(content, to)

class UrgentMessage(Message):
    def notify(self, to: str, content: str):
        self._sender.send(f"[紧急] {content}", to)

5.3 使用

sms = SmsSender()
email = EmailSender()
NormalMessage(sms).notify("13800138000", "你好")
UrgentMessage(email).notify("user@example.com", "请尽快处理")
# 任意 消息类型 × 发送渠道 组合,无需 N×M 个类

六、桥接 vs 继承枚举

方式 类数量 扩展
继承枚举所有组合 维度1 × 维度2 × … 加一维就要乘一批类
桥接(抽象 + 实现分离) 维度1 的类数 + 维度2 的类数 各维独立加类即可

记忆:桥接用组合把两个维度连起来,用继承分别扩展每一维,避免“乘起来”的子类。


七、桥接 vs 其他模式

模式 目的 与桥接的区别
桥接 抽象与实现分离,两维独立变化 强调“两个维度”,用组合连接
适配器 让不兼容接口能一起用 侧重接口转译,通常不涉及“两维独立扩展”
策略 算法可替换 往往只有一个“策略”维度,桥接常有两维(抽象 + 实现)

八、常见问题与注意点

8.1 谁算“抽象”、谁算“实现”?

抽象:更偏业务、高层、会“用”底层能力的那一侧(形状、遥控器、消息类型)。
实现:更偏技术、底层、真正干活的那一侧(颜色/绘制、设备、发送渠道)。
同一种划分在不同业务里可以互换,只要保证“两维独立变化、用组合连接”即可。

8.2 抽象和实现都要接口吗?

在 Python 里不强制用 ABC;只要“抽象侧”依赖的是“某一类能力”(如会 fill、会 turn_on),实现侧提供这些能力即可。用 ABC 可以让“抽象/实现”的约定更清晰。

8.3 运行时换“实现”

因为实现是组合进来的,所以可以在运行时换:例如 shape._color = Green(),就变成“绿色形状”;不必新增类。


九、小结

  • 桥接模式:把抽象(高层)和实现(底层)分离,通过组合连接,使两维能独立变化,避免类爆炸。
  • 四个角色:抽象、精化抽象、实现者、具体实现;抽象持有实现者,调用时委托给实现者。
  • 典型用途:形状×颜色、遥控×设备、消息×发送渠道等“多维度独立变化”的场景。
  • 核心:用组合代替“继承出所有组合”,类数从乘变加。

建议先写“形状×颜色”一例,再试“遥控×设备”或“消息×发送”,体会“加一个颜色/加一个形状”只加一个类、不互相乘,这样对桥接模式会掌握得比较扎实。

发表评论