【设计模式】桥接模式(柄体模式,接口模式)
桥接模式(Bridge Pattern)详解
一、桥接模式简介
桥接模式(Bridge Pattern) 是一种 结构型设计模式(对象结构型模式),它将抽象部分与其实现部分分离,使它们都可以独立地变化。简单来说,就是将一个大类拆分成两个或多个独立的类层次结构,以便这些层次可以独立扩展。
在桥接模式中,“桥”是连接抽象和实现的接口,允许两者独立发展。
桥接模式:将抽象部分与它的实现部分解耦,使得两者都能够独立变化。
又被称为柄体(Handle and Body)模式或接口(Interface)模式
用抽象关联取代了传统的多层继承
将类之间的静态继承关系转换为动态的对象组合关系
简述案例:
分析:
蜡笔:颜色和型号两个不同的变化维度(即两个不同的变化原因)耦合在一起,无论是对颜色进行扩展还是对型号进行扩展都势必会影响另一个维度
毛笔:颜色和型号实现了分离,增加新的颜色或者型号对另一方没有任何影响
桥接模式的结构
桥接模式包含以下4个角色:
Abstraction(抽象类)
RefinedAbstraction(扩充抽象类)
Implementor(实现类接口)
ConcreteImplementor(具体实现类)
(如下图结构)
上述毛笔结构示意图:
(桥接模式的实现)

二、解决的问题类型
桥接模式主要用于解决以下问题:
- 多维度的变化:当一个类依赖于另一个类,并且这两个类都需要根据不同的需求进行扩展时。
- 避免代码膨胀:如果不使用桥接模式,为了支持不同类型的组合,你可能需要创建大量的子类,导致“类爆炸”。
三、使用场景
- 需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系:例如,这允许你独立地选择平台和设备的不同型号。
- 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充:这时可以结合使用桥接模式。
- 原始设计方案违反了开闭原则:即软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
- 抽象部分和实现部分可以以继承的方式独立扩展而互不影响
一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立地进行扩展
不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统
四、实际案例
假设我们正在开发一个绘图应用,这个应用需要支持多种图形(如圆形、矩形等),同时还要支持不同的渲染方式(如矢量渲染、光栅化渲染)。如果不用桥接模式,我们就需要为每种图形和每种渲染方式的组合创建一个具体的类,这样会导致类的数量迅速增加。
通过使用桥接模式,我们可以将图形和渲染方式分开处理,使得新的图形类型或者新的渲染方式可以很容易地添加进来,而不需要修改现有的代码。
五、代码案例
// 渲染器接口
interface Renderer {public void renderCircle(float radius);
}// 具体的渲染器实现
class VectorRenderer implements Renderer {@Overridepublic void renderCircle(float radius) {System.out.println("Drawing a circle of radius " + radius);}
}class RasterRenderer implements Renderer {@Overridepublic void renderCircle(float radius) {System.out.println("Drawing pixels for a circle of radius " + radius);}
}// 形状抽象类
abstract class Shape {protected Renderer renderer;// 构造函数注入渲染器public Shape(Renderer renderer) {this.renderer = renderer;}public abstract void draw();public abstract void resize(float factor);
}// 圆形的具体实现
class Circle extends Shape {private float radius;public Circle(Renderer renderer, float radius) {super(renderer);this.radius = radius;}@Overridepublic void draw() {renderer.renderCircle(radius);}@Overridepublic void resize(float factor) {radius *= factor;}
}// 测试代码
public class Test {public static void main(String[] args) {Renderer vectorRenderer = new VectorRenderer();Shape circle = new Circle(vectorRenderer, 5);circle.draw(); // 输出: Drawing a circle of radius 5circle.resize(2);circle.draw(); // 输出: Drawing a circle of radius 10Renderer rasterRenderer = new RasterRenderer();Shape rasterCircle = new Circle(rasterRenderer, 5);rasterCircle.draw(); // 输出: Drawing pixels for a circle of radius 5}
}
典型的实现类接口代码(c++):
interface Implementor
{void OperationImpl();
}
典型的具体实现类代码:
class ConcreteImplementor : Implementor
{public void OperationImpl(){//具体业务方法的实现}
}
典型的抽象类代码:
abstract class Abstraction
{protected Implementor impl; //定义实现类接口对象public void SetImpl(Implementor impl) {this.impl = impl;}public abstract void Operation(); //声明抽象业务方法
}
典型的扩充抽象类(细化抽象类)代码:
class RefinedAbstraction : Abstraction
{public override void Operation() {//业务代码impl.OperationImpl(); //调用实现类的方法//业务代码}
}
应用案例二
某软件公司要开发一个跨平台图像浏览系统,要求该系统能够显示BMP、JPG、GIF、PNG等多种格式的文件,并且能够在Windows、Linux、UNIX等多个操作系统上运行。系统首先将各种格式的文件解析为像素矩阵(Matrix),然后将像素矩阵显示在屏幕上,在不同的操作系统中可以调用不同的绘制函数来绘制像素矩阵。另外,系统需具有较好的扩展性,以便在将来支持新的文件格式和操作系统。试使用桥接模式设计该跨平台图像浏览系统。
更多案例
- 毛笔案例
现需要提供大中小3种型号的画笔,能够绘制5种不同颜色,如果使用蜡笔,我们需要准备3*5=15支蜡笔,也就是说必须准备15个具体的蜡笔类。而如果使用毛笔的话,只需要3种型号的毛笔,外加5个颜料盒,用3+5=8个类就可以实现15支蜡笔的功能。本实例使用桥接模式来模拟毛笔的使用过程。
- 跨平台视频播放器
如果需要开发一个跨平台视频播放器,可以在不同操作系统平台(如Windows、Linux、Unix等)上播放多种格式的视频文件,常见的视频格式包括MPEG、RMVB、AVI、WMV等。现使用桥接模式设计该播放器。
实际模式应用:
Java语言通过Java虚拟机实现了平台的无关性。
六、优缺点
优点 | 描述 |
---|---|
分离抽象与实现 | 让抽象和实现都能够独立扩展,互不影响。 |
增强灵活性 | 可以灵活地组合不同的抽象和实现,适应更多的应用场景。 |
其他 | 可以取代多层继承方案,极大地减少了子类的个数 |
其他 | 提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,不需要修改原有系统,符合开闭原则 |
缺点 | 描述 |
---|---|
增加了系统的复杂度 | 对于简单的场景,使用桥接模式可能会显得过于复杂。增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程 |
需要额外的设计工作 | 需要仔细考虑如何划分抽象和实现,以及它们之间的接口定义。正确识别出系统中两个独立变化的维度并不是一件容易的事情 |
七、最终小结
桥接模式是一种非常有用的模式,尤其适用于那些需要跨越多个维度进行扩展的系统。通过分离抽象和实现,它可以有效地减少系统中的类数量,提高系统的可维护性和可扩展性。然而,在应用桥接模式时,也需要考虑到其带来的额外复杂性,确保这种复杂性不会超过它所带来的好处。对于初学者而言,理解并正确运用桥接模式可能需要一些时间,但一旦掌握,就能显著提升你的设计能力。
八、扩展
- 适配器模式与桥接模式的联用:桥接模式和适配器模式用于设计的不同阶段,桥接模式用于系统的初步设计,对于存在两个独立变化维度的类可以将其分为抽象化和实现化两个角色,使它们可以分别进行变化;而在初步设计完成之后,当发现系统与已有类无法协同工作时,可以采用适配器模式。但有时候在设计初期也需要考虑适配器模式,特别是那些涉及到大量第三方应用接口的情况。
部分内容由AI大模型生成,注意识别!