概念
桥接模式(Bridge Pattern)是设计模式中最复杂的模式之一,它把事物对象和该对象的具体行为、具体特征分离开来,使它们可以独自进行变化和更改。这样的解释可能比较抽象和令人费解,读者可以通过下面的应用场景进一步来理解。
应用场景
我们还以绘制图形为例,比如我们要绘制矩形Rectangle、Circle、Triangle,我们需要至少需要定义3个形状类来表示,如果我们要绘制的图形还有不同的颜色,如Red、Yello、Green等,此时我们可能有一下两种设计方案:
- 为每个形状类都提供颜色的实现,比如通过继承增加RedRectangle、YellowRectangle、RedCircle、YellowCircle等
- 根据实际需要对具体
形状对象
和颜色属性
进行组合,把颜色属性
作为参数动态给到形状对象
来使用,比如红色的圆形就是组合 Circle + RedAttr
如果一个有两个及以上的变化维度的系统(或对象),采用方案2可以明显减少类的维护个数,扩展起来更为方便。方案2即为桥接模式的应用,它使用组合关联关系,而不是通过继承来做扩展,这减少了类之间的耦合,减少代码的编写量。
用过Java Swing进行过图形界面开发的读者发现,界面的风格(Look and Feel)在不同操作系统下是不一样的,实际上Java为每一个GUI组件都提供了一个Peer构件,该构件在不同操作系统下有不同的实现,Swing的Peer架构就是应用了桥接模式。
在以下情况下我们可以尝试使用桥接模式:
- 一个类存在两个独立变化的维度,且这两个维度都需要独立扩展,这样有利于开发者单独去维护维度的扩展,而不需要关注其他维度的实现
- 继承,尤其是多层次的继承会使得类的个数和理解复杂度都急剧增加,此时桥接模式尤为适用
结构桥接模式包含以下角色:
- Abstraction:抽象类
- RefinedAbstraction:扩充抽象类
- Implementor:扩展行为或属性的接口定义(这里需要特别注意,这是Abstraction的行为或特征的实现类接口,翻译后确实词不达意)
- ConcreteImplementor:Implementor的具体实现类
桥接模式就是将Abstraction的一些行为或属性独立的再抽象到Implementor中,在Abstraction(或RefinedAbstraction)中通过组合来动态的将Implementor的实现ConcreteImplementor组合起来,而不是用典型的继承关系来做扩展。这样就实现了一个对象(或系统)跟它的行为(或属性)能够通过不同的类单独去扩展和变化,再通过引用关系组合起来,这就是所谓的桥接。
代码示例
/** "Implementor" */
interface DrawingAPI {
public void drawCircle(double x, double y, double radius);
}
/** "ConcreteImplementorA" 1/2 */
class DrawingAPI1 implements DrawingAPI {
public void drawCircle(double x, double y, double radius) {
System.out.printf("API1.circle at %f:%f radius %f\n", x, y, radius);
}
}
/** "ConcreteImplementorB" 2/2 */
class DrawingAPI2 implements DrawingAPI {
public void drawCircle(double x, double y, double radius) {
System.out.printf("API2.circle at %f:%f radius %f\n", x, y, radius);
}
}
/** "Abstraction" */
interface Shape {
public void draw(); // low-level
public void resizeByPercentage(double pct); // high-level
}
/** "Refined Abstraction" */
class CircleShape implements Shape
{
private double x, y, radius;
private DrawingAPI drawingAPI;
public CircleShape(double x, double y, double radius, DrawingAPI drawingAPI) {
this.x = x;
this.y = y;
this.radius = radius;
this.drawingAPI = drawingAPI;
}
// low-level i.e. Implementation specific
public void draw() {
drawingAPI.drawCircle(x, y, radius);
}
// high-level i.e. Abstraction specific
public void resizeByPercentage(double pct) {
radius *= pct;
}
}
/** "Client" */
class BridgePatternDemo {
public static void main(String[] args) {
Shape[] shapes = new Shape[2];
shapes[0] = new CircleShape(1, 2, 3, new DrawingAPI1());
shapes[1] = new CircleShape(5, 7, 11, new DrawingAPI2());
for (Shape shape : shapes) {
shape.resizeByPercentage(2.5);
shape.draw();
}
}
}
输出:
API1.circle at 1:2 7.5
API2.circle at 5:7 27.5
优缺点
优点:
-
桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法。
-
桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
-
实现细节对客户透明,可以对用户隐藏实现细节。
缺点:
- 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进
行设计与编程。
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
作业
最后,我们给读者留一个作业:我们需要开发一个跨平台的视频播放器,在不同操作系统平台(Windows、Linux、MacOS等)上,除了可以播放多种格式的视频文件(MPEG、RMVB、AVI、WMV等),还可以播放多种格式的音频文件(WAV、MP3、AAC、OGG等),该如何进行设计呢?