桥接模式
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"的适配器。
总结
当你发现一个类存在两个或多个独立变化的维度,并且你不想使用继承导致类爆炸时,就应该立刻想到桥接模式 。它通过组合/聚合关系代替继承,在两个维度之间架起一座灵活的桥梁,从而构建出松耦合、高扩展性的系统。