什么是桥接模式?它解决什么问题?
简化版
桥接模式是把抽象部分和实现部分拆开,让它们可以独立变化的结构型设计模式。它主要解决多维度变化导致的类爆炸问题,比如“形状 × 颜色”“消息类型 × 发送渠道”这种组合不断增加的场景。
详细版
桥接模式的核心思想是:不要把所有变化都压进同一条继承链,而是把其中一个变化维度抽成接口,通过组合把两个维度连接起来。
典型例子是消息发送系统:
- 抽象维度:普通消息、紧急消息、验证码消息。
- 实现维度:短信、邮件、站内信、企业微信。
- 如果用继承,可能出现
UrgentSmsMessage、UrgentEmailMessage、CodeSmsMessage、CodeEmailMessage等大量类。 - 如果用桥接,消息类型持有一个发送渠道接口,新增消息类型和新增发送渠道互不影响。
桥接模式强调两个关键词:
- 抽象:面向业务语义的高层行为。
- 实现:完成底层能力的可替换实现。
它不是为了“多写一个接口”,而是为了让两个变化方向从继承关系中解耦出来。
完整版教学
一、为什么会出现桥接模式
很多设计一开始看起来很自然:有一个父类,下面按业务类型拆子类。问题出现在业务变化维度不止一个的时候。
例如图形绘制:
- 图形有圆形、矩形、三角形。
- 绘制方式有 SVG、Canvas、OpenGL。
如果用继承表达所有组合,就会变成:
SvgCircleCanvasCircleOpenGLCircleSvgRectangleCanvasRectangleOpenGLRectangle
当有 3 种图形和 3 种绘制方式时,需要 9 个类;如果变成 6 种图形和 5 种绘制方式,就可能膨胀到 30 个类。类的数量不是线性增长,而是两个维度相乘。
桥接模式就是为这种问题准备的:把图形和绘制方式拆成两个维度,让图形对象通过组合持有绘制接口。
二、桥接模式的定义怎么理解
桥接模式通常被描述为:将抽象部分与它的实现部分分离,使它们都可以独立变化。
这里的“抽象”和“实现”不是简单等同于 Java 的 abstract class 和 interface,而是设计层面的两个角色:
- 抽象部分:面向业务使用者的高层概念,比如消息、图形、报表。
- 实现部分:负责完成实际工作的底层能力,比如发送渠道、绘图 API、导出格式。
桥接中的“桥”就是抽象对象内部持有的实现接口引用。抽象对象把一部分工作委托给实现接口,自己保留业务语义和流程控制。
三、它解决的不是所有继承问题
桥接模式不是看到继承就要替换。它最适合处理两个或多个稳定变化维度的组合问题。
如果只有一种变化,例如只是不停新增不同的消息类型,用普通继承、策略模式或模板方法可能就够了。桥接模式的价值在于:两个维度都可能扩展,而且它们之间不应该互相绑定。
判断是否适合桥接,可以问三个问题:
- 是否存在两个独立变化方向?
- 两个方向组合后是否导致类数量快速增长?
- 高层业务是否只依赖某个实现接口,而不应该关心具体实现类?
三个答案都比较明确时,桥接模式就很合适。
四、桥接模式和组合优于继承
桥接模式是“组合优于继承”的典型体现。继承把能力固定到类层级上,组合则把能力放到对象关系里。
继承方案的问题是:一旦父子类层级确定,新增另一个维度就很难优雅塞进去。组合方案的问题少一些:抽象类只持有一个实现接口,运行时可以注入不同实现。
比如:
abstract class Message {
protected MessageSender sender;
}
Message 不知道底层是短信还是邮件,只知道有一个 MessageSender 可以发送内容。这就是桥。
五、面试时怎么答得稳
面试回答桥接模式时,不要只背“抽象和实现分离”。更好的回答顺序是:
- 先说它是结构型模式,核心是用组合连接两个变化维度。
- 再说明它解决类爆炸和继承层级过深的问题。
- 然后举一个“消息类型 × 发送渠道”或“图形 × 绘制方式”的例子。
- 最后补一句:它让抽象和实现都能独立扩展,符合开闭原则。
这样回答既有定义,也有动机和场景,面试官一般会继续追问实现方式或与适配器模式的区别。
六、常见误区与追问
桥接模式把抽象层次与实现层次拆成可独立扩展的两个类族,并用组合连接。假设遥控器有2种、设备有3种,交叉继承可能需要6个组合子类;桥接只维护2个遥控器与3个设备类型。这里的“实现”指设备能力维度,不是某个抽象方法的方法体。
| 检查维度 | 判定依据 |
|---|---|
| 要解决的问题 | 两个维度交叉导致类爆炸 |
| 核心手段 | 抽象持有实现接口并委托 |
Abstraction o---- Implementor(组合桥)
记忆钩子:把一棵交叉继承树拆成两棵树,中间用对象引用架桥。
- 误区:桥接模式等同于接口编程。 接口只是手段,模式还要求识别并解耦两个独立变化维度。
- 追问:为什么它属于结构型模式? 它重组两组对象的静态依赖和组合方式。
- 误区:桥接只能在系统设计初期使用。 发现交叉子类后也可重构,但迁移成本会更高。
- 追问:桥接与依赖注入是什么关系? 依赖注入可完成 Implementor 装配,但不替代维度划分。
- 追问:什么情况下不该用? 只有一个稳定实现、没有交叉扩展压力时,额外桥接会过度设计。
七、加强记忆
记桥接模式时抓住“两个维度,一座桥”:当一个业务对象既有高层抽象变化,又有底层实现变化,并且两者组合会产生大量子类时,就把一个维度抽成实现接口,让抽象对象通过组合去连接它。