桥接模式
1. 核心思想:一句话点题
桥接模式的核心是:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
这句话听起来很官方,我们把它翻译成大白话:当一个事物有两个或多个独立变化的维度时,不要用继承,而是用组合的方式将它们连接起来。这个“连接”就是“桥”。
2. 问题场景:为什么需要桥接模式?
想象一个场景:我们要创建不同形状(Shape),并且每个形状可以有不同的颜色(Color)。
维度一:形状 (Circle, Square, Triangle...) 维度二:颜色 (Red, Blue, Green...)
如果我们用传统的继承方式来实现,会发生什么?
// 继承会导致“类爆炸” public abstract class Shape {} public class RedCircle extends Shape {} public class BlueCircle extends Shape {} public class GreenCircle extends Shape {} public class RedSquare extends Shape {} public class BlueSquare extends Shape {} public class GreenSquare extends Shape {} // ... 如果再加一个Triangle,就要再加3个类 // ... 如果再加一个Yellow,就要再给每个形状都加一个类
问题显而易见:
类爆炸(Class Explosion):类的数量会是“形状数量 × 颜色数量”。每增加一个形状或一个颜色,都需要创建一堆新类。
扩展性极差:如果想增加一个新的形状(比如三角形),就必须为所有已存在的颜色都创建一个对应的三角形子类。反之亦然。
违反单一职责原则:RedCircle 这个类同时承担了定义“形状是圆形”和“颜色是红色”两个职责,耦合度太高。
桥接模式就是为了解决这个问题的。它建议我们把这两个维度分开。
3. 桥接模式的解决方案
桥接模式说:“形状”和“颜色”是两个独立变化的东西,我们应该把它们分开,然后让“形状”持有一个“颜色”的引用。
抽象部分(Abstraction): 形状(Shape)。它负责高层逻辑,比如“画一个形状”。它内部包含一个指向“实现部分”的引用。
实现部分(Implementation): 颜色(Color)。它负责底层具体的实现,比如“填充红色”。
这个“持有引用”的动作,就像在“形状”和“颜色”之间架起了一座桥梁。

我们来对应一下图中的角色:
Abstraction (抽象部分): 我们的 Shape 类。它包含一个 Implementor 的引用。
RefinedAbstraction (精确抽象部分): 具体的形状,如 Circle, Square。它们继承自 Shape。
Implementor (实现部分接口): 我们的 Color 接口。它定义了实现需要遵循的方法,比如 applyColor()。
ConcreteImplementor (具体实现部分): 具体的颜色,如 RedColor, BlueColor。它们实现了 Color 接口。
4. 代码实例(Java)
让我们用代码来实现上面的“形状与颜色”的例子。
第1步:创建实现部分(Implementor)的接口和具体实现
这是变化的第一个维度:颜色。
// Implementor: 颜色接口 public interface Color { String applyColor(); } // ConcreteImplementor: 红色 public class RedColor implements Color { @Override public String applyColor() { return "涂上了红色"; } } // ConcreteImplementor: 蓝色 public class BlueColor implements Color { @Override public String applyColor() { return "涂上了蓝色"; } }
第2步:创建抽象部分(Abstraction)和它的引用(桥)
这是变化的第二个维度:形状。注意,Shape 类中包含了一个 Color 引用,这就是桥梁。
// Abstraction: 形状抽象类 public abstract class Shape { // 关键!这就是桥梁,将Shape和Color连接起来 protected Color color; public Shape(Color color) { this.color = color; } // 抽象方法,由子类实现 public abstract String draw(); }
第3步:创建精确抽象部分(Refined Abstraction)
这些是具体的形状,它们继承 Shape 并使用 color 引用来完成自己的功能。
// RefinedAbstraction: 圆形 public class Circle extends Shape { public Circle(Color color) { super(color); // 调用父类构造函数,把“桥”搭好 } @Override public String draw() { return "绘制一个圆形,并且" + color.applyColor(); } } // RefinedAbstraction: 正方形 public class Square extends Shape { public Square(Color color) { super(color); } @Override public String draw() { return "绘制一个正方形,并且" + color.applyColor(); } }
第4步:客户端调用
现在,我们可以自由地组合形状和颜色了。
public class BridgePatternDemo { public static void main(String[] args) { // 创建一个红色的圆形 Shape redCircle = new Circle(new RedColor()); System.out.println(redCircle.draw()); // 输出: 绘制一个圆形,并且涂上了红色 // 创建一个蓝色的正方形 Shape blueSquare = new Square(new BlueColor()); System.out.println(blueSquare.draw()); // 输出: 绘制一个正方形,并且涂上了蓝色 // 现在,想创建一个蓝色的圆形,非常容易! Shape blueCircle = new Circle(new BlueColor()); System.out.println(blueCircle.draw()); // 输出: 绘制一个圆形,并且涂上了蓝色 // 如果要增加一个新的形状“三角形”,只需要: // 1. 创建一个 Triangle 类继承 Shape // 完全不需要动任何颜色的代码! // 如果要增加一个新的颜色“绿色”,只需要: // 1. 创建一个 GreenColor 类实现 Color 接口 // 完全不需要动任何形状的代码! } }
5. 优点与缺点
优点:
分离抽象和实现:这是最核心的优点,让两边可以独立地扩展,互不影响。
极大地提高了扩展性:如上例所示,增加新形状或新颜色都变得非常简单。
符合单一职责原则:每个类的职责更清晰。Shape 管形状,Color 管颜色。
对客户端隐藏实现细节:客户端(main方法)只需要和 Shape 这个高层抽象打交道,不需要知道具体的 Color 是如何实现的。
缺点:
增加了系统的复杂性:引入了更多的类和接口,如果系统本身很简单,使用桥接模式可能会让设计变得过度复杂。
理解难度:对于新手来说,需要理解“抽象”和“实现”的分离,比简单的继承要多绕一个弯。
6. 与适配器模式(Adapter)的区别
这是一个非常常见的混淆点。
目的不同:
桥接(Bridge):是设计时的决策。目的是分离抽象和实现,让它们可以独立变化。它是一种主动的、有预谋的架构设计。
适配器(Adapter):是补救时的措施。目的是让两个已经存在但互不兼容的接口能够协同工作。它是一种被动的、为了解决兼容性问题的补丁。
结构不同:
桥接模式是两个独立变化的维度通过组合连接。
适配器模式是通过封装一个对象,提供一个不同的接口来包装它。
一个比喻:
桥接:你正在设计一个全新的笔记本电脑,你决定使用通用的 Type-C 接口(实现部分),这样你的电脑(抽象部分)未来就可以连接任何符合 Type-C 标准的设备(显示器、充电器、硬盘等)。
适配器:你有一台只有 USB-A 接口的老电脑,现在买了一个只有 Type-C 接口的新硬盘。为了让它们能工作,你去买了一个“USB-A 转 Type-C”的适配器。
总结
当你发现一个类存在两个或多个独立变化的维度,并且你不想使用继承导致类爆炸时,就应该立刻想到桥接模式。它通过组合/聚合关系代替继承,在两个维度之间架起一座灵活的桥梁,从而构建出松耦合、高扩展性的系统。