当前位置: 首页 > news >正文

GoF设计模式——桥接模式

本文是【GoF设计模式】系列第10篇,更多内容欢迎关注公众号:咖啡八杯

image

前言

为什么需要桥接模式?

假设要做一个图形编辑器,有圆形、矩形、三角形三种图形,每种图形又要支持红色、蓝色、黄色三种颜色。用继承来实现,就要写 RedCircleBlueCircleRedRectangleBlueRectangle... 类的数量 = 图形数 × 颜色数,3 × 3 = 9 个类。再加一种图形或一种颜色,类就会爆炸式增长。

// 继承方案:类爆炸
class RedCircle extends Circle { ... }
class BlueCircle extends Circle { ... }
class RedRectangle extends Rectangle { ... }
// ... 9 个类,再加一个图形或颜色就要翻倍

两种选择:要么忍受类爆炸,要么把颜色作为字段写在图形类里——后者看似简单,但每种颜色逻辑都要在图形类内部判断,代码会变得臃肿,加一种颜色就要改所有图形类。

这种"两个维度各自变化,组合后类爆炸"的矛盾,就是桥接模式要解决的问题。

概念

桥接模式(Bridge Pattern)是一种结构型设计模式,核心思想是将抽象与实现分离到两个独立的维度,使它们可以各自独立扩展

桥接模式包含四个角色:

  • Abstraction(抽象):定义抽象部分的接口,持有对实现层的引用(这就是"桥")。通常用抽象类,因为需要持有引用并提供默认逻辑。
  • RefinedAbstraction(修正抽象):对抽象接口进行扩展,是抽象的具体变体。
  • Implementor(实现):定义实现部分的接口,与抽象层的接口可以完全不同。
  • ConcreteImplementor(具体实现):实现实现化接口的具体类。
classDiagramdirection BTclass Abstraction {<<abstract>>-impl: Implementor+operation()}class RefinedAbstraction {+operation()}class Implementor {<<interface>>+operationImpl()}class ConcreteImplementorA {+operationImpl()}class ConcreteImplementorB {+operationImpl()}RefinedAbstraction --|> Abstraction : 继承Abstraction o--> Implementor : 持有(桥)ConcreteImplementorA ..|> Implementor : 实现ConcreteImplementorB ..|> Implementor : 实现

Abstraction 内部持有 Implementor 的引用,这是"桥"的关键——通过组合而非继承连接两个维度。RefinedAbstraction 扩展抽象层,ConcreteImplementorA/B 实现实现层,两边各自独立发展。

可以把桥接模式理解为遥控器和电视的关系:遥控器是抽象层,电视是实现层。遥控器内部持有一个电视的引用,按下"开机"键时调用电视的开机方法。不同的遥控器(基础遥控器、万能遥控器)是修正抽象,不同品牌的电视(小米、索尼)是具体实现。这样遥控器和电视可以各自独立发展——新增一个电视品牌不需要改遥控器的代码,新增一种遥控器也不需要改电视的代码。

实现

桥接模式只有一种实现方式:抽象层持有实现层的引用,通过组合而非继承来关联两个维度。

基础实现

实现步骤:定义实现层接口,创建具体实现类;定义抽象层基类(抽象类),内部持有实现层引用;创建修正抽象类扩展抽象层。

// ===== 实现层接口 =====
interface Implementor {public void operationImpl();
}// ===== 具体实现A =====
class ConcreteImplementorA implements Implementor {public void operationImpl() {System.out.println("实现方式A");}
}// ===== 具体实现B =====
class ConcreteImplementorB implements Implementor {public void operationImpl() {System.out.println("实现方式B");}
}// ===== 抽象层基类(用抽象类,因为需要持有实现层引用) =====
abstract class Abstraction {protected Implementor impl;  // 桥:持有实现层的引用public Abstraction(Implementor impl) {this.impl = impl;}public void operation() {impl.operationImpl();  // 委托给实现层}
}// ===== 修正抽象 =====
class RefinedAbstraction extends Abstraction {public RefinedAbstraction(Implementor impl) {super(impl);}public void operation() {// 可以在委托前后加自己的逻辑System.out.println("RefinedAbstraction 额外逻辑");super.operation();}
}// ===== 客户端 =====
public class Client {public static void main(String[] args) {Implementor implA = new ConcreteImplementorA();Abstraction abs = new RefinedAbstraction(implA);abs.operation();}
}

引入一个例子:「一台万能遥控器,手里有小米电视和索尼电视两台设备。遥控器内部有个"电视插槽",插哪台电视就能控制哪台——遥控器本身不用改代码,电视也不用改接口」。

遥控器对应 Abstraction(抽象层),电视对应 Implementor(实现层),"插槽"对应构造方法注入的组合关系。换一台电视只需把新实例传给遥控器,两边各自独立。

// 实现层:电视品牌
interface TV {public void powerOn();public void powerOff();
}class XiaomiTV implements TV {public void powerOn() {System.out.println("小米电视开机");}public void powerOff() {System.out.println("小米电视关机");}
}class SonyTV implements TV {public void powerOn() {System.out.println("索尼电视开机");}public void powerOff() {System.out.println("索尼电视关机");}
}// 抽象层:遥控器
abstract class RemoteControl {protected TV tv;  // 持有电视的引用(桥)public RemoteControl(TV tv) {this.tv = tv;}public void turnOn() {tv.powerOn();}public void turnOff() {tv.powerOff();}
}// 修正抽象:万能遥控器(额外功能)
class UniversalRemote extends RemoteControl {public UniversalRemote(TV tv) {super(tv);}public void mute() {System.out.println("静音");}
}// 使用:遥控器和电视自由组合
RemoteControl basic = new RemoteControl(new XiaomiTV());
basic.turnOn();  // 控制小米电视UniversalRemote universal = new UniversalRemote(new SonyTV());
universal.turnOn();
universal.mute();  // 万能遥控器额外功能

为什么 Abstraction 用抽象类而不是接口? 桥接模式的抽象层需要:

  1. 持有实现层的引用:接口不能有实例字段,只有抽象类才能保存 Implementor 实例
  2. 提供默认的委托逻辑operation() 方法通常直接委托给 impl,写在抽象类中避免子类重复代码

简单说:接口定义"能做什么",抽象类定义"怎么做框架"。桥接模式的抽象层需要既有状态(持有引用)又有行为(默认委托),所以用抽象类更合适。

桥接模式与简单组合的区别

桥接模式本质上就是组合,但它和普通的"一个类持有另一个类"有本质区别:

维度 简单组合 桥接模式
目的 复用功能、委托调用 分离两个独立变化的维度
结构 无约束,随意组合 抽象层 + 实现层 + 桥(持有引用)
扩展性 通常只有一方可扩展 两边各自独立扩展

举个例子:

// 简单组合:订单持有支付方式的引用
class Order {private PaymentMethod payment;  // 组合:只是委托public void checkout() {payment.pay(amount);}
}

订单和支付方式之间没有"两个维度各自变化"的关系——订单只有一个维度,支付方式也只有一个维度。目的只是"委托支付",不是解决类爆炸。

而桥接模式的图形 + 渲染:

// 桥接:图形持有渲染器的引用
abstract class Shape {protected Renderer renderer;  // 桥:维度分离public void draw() {renderer.render(this);}
}

图形是一个维度(圆形、矩形...),渲染是另一个维度(矢量、像素...),两边各自独立扩展——新增一种图形不影响渲染层,新增一种渲染不影响图形层。

判断标准:新增一个实现类,抽象层是否需要新增对应的子类?

  • 简单组合:新增 ApplePay,订单类不动——一方扩展,另一方不动
  • 桥接模式:新增渲染方式,图形类不动;新增图形,渲染类不动——两边都可以独立扩展

记忆口诀组合是手段,桥接是结构。组合解决"复用",桥接解决"维度分离"。

总结

桥接模式本质上是用组合替代继承来解决"两个维度各自变化"的问题——把乘法关系的类数量变成加法关系。

什么时候用

  • 一个类有两个独立变化的维度(如图形和颜色、支付方式和优惠策略)
  • 每个维度都可能独立扩展,不希望类数量爆炸
  • 需要在运行时动态切换实现层(如换一个电视品牌)

什么时候不用

  • 只有一个变化维度,用继承或简单组合就够了
  • 两个维度耦合紧密,分开反而增加理解成本
  • 系统简单,过早使用桥接模式会增加复杂度

简单记忆

桥接拆维度,组合替继承。两个方向各自变,类数从乘变加法。

模式 接口关系 核心意图 典型场景
桥接 抽象持有实现引用 分离两个独立变化的维度 图形+渲染、消息+渠道
适配器 目标接口 ≠ 被包装对象接口 转换接口,让不兼容的类协同 第三方库集成
策略 上下文持有策略引用 替换同一维度的不同算法 排序算法、折扣计算
抽象工厂 工厂接口定义创建方法 创建一系列相关对象 跨数据库 DAO

口诀对比:桥接拆维度,适配改接口,策略换算法,工厂管创建。

桥接 vs 适配器

维度 桥接模式 适配器模式
核心意图 事前设计,分离两个独立变化的维度 事后补救,让不兼容的接口协同工作
结构差异 抽象层持有实现层引用,双方接口可不同 适配器实现目标接口,持有被适配对象
关注点 预防类爆炸,支持独立扩展 解决接口不兼容问题
典型场景 JDBC 驱动、跨平台 UI 第三方库集成、遗留系统对接

逐步区分法

  • 如果两个维度都可能独立扩展 → 选桥接
  • 如果只是要让现有类协同工作 → 选适配器
  • 如果需要事后补救接口不兼容 → 选适配器

桥接 vs 策略

维度 桥接模式 策略模式
核心意图 分离两个独立变化的维度 替换同一维度的不同算法
结构差异 抽象层持有实现层引用,两者是平行的维度 上下文持有策略引用,策略是算法的变体
关注点 维度分离,独立扩展 算法替换,行为可变
典型场景 图形+渲染、消息+渠道 排序算法、折扣计算

逐步区分法

  • 如果有两个独立变化的维度 → 选桥接
  • 如果只是同一个行为的不同实现方式 → 选策略
  • 如果关注点是"用什么算法" → 选策略
  • 如果关注点是"用什么实现体系" → 选桥接

桥接 vs 抽象工厂

维度 桥接模式 抽象工厂模式
核心意图 分离两个独立变化的维度 创建一系列相关对象
结构差异 抽象层持有实现层引用 工厂接口定义创建方法
关注点 结构分离,运行时切换 对象创建,产品族约束
典型场景 JDBC 驱动、跨平台 UI 跨数据库 DAO、跨 UI 主题

逐步区分法

  • 如果需要在运行时切换实现 → 选桥接
  • 如果只需要创建一系列相关对象 → 选抽象工厂
  • 如果关注点是"对象怎么创建" → 选抽象工厂
  • 如果关注点是"实现怎么切换" → 选桥接

练习题目

图形渲染系统

题目描述:一个图形系统需要支持多种图形和多种渲染方式。图形包括圆形(Circle)和矩形(Rectangle),渲染方式包括矢量渲染(Vector)和像素渲染(Pixel)。请使用桥接模式实现,使得图形类型和渲染方式可以独立扩展。

输入描述:第一行是一个整数 N(1 ≤ N ≤ 100),表示后面有 N 行输入。接下来的 N 行,每行包含两个字符串,第一个表示图形类型(Circle / Rectangle),第二个表示渲染方式(Vector / Pixel)。

输出描述:对于每行输入,输出该图形使用该渲染方式的绘制结果。

输入示例

6
Circle Vector
Circle Pixel
Rectangle Vector
Rectangle Pixel
Circle Vector
Rectangle Pixel

输出示例

Drawing Circle with Vector Renderer
Drawing Circle with Pixel Renderer
Drawing Rectangle with Vector Renderer
Drawing Rectangle with Pixel Renderer
Drawing Circle with Vector Renderer
Drawing Rectangle with Pixel Renderer

解题思路:如果不使用桥接模式,就要为每种组合创建类(VectorCirclePixelCircleVectorRectanglePixelRectangle),类数量 = 图形数 × 渲染数。使用桥接模式,渲染方式是实现层(Renderer 接口),图形是抽象层(Shape 抽象类持有 Renderer 引用),通过构造方法注入。这样图形和渲染各自独立扩展,类数量从乘法变成加法。

import java.util.*;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();while (n-- > 0) {String shapeType = sc.next();String rendererType = sc.next();// 实现层:渲染方式Renderer r = null;if ("Vector".equals(rendererType)) {r = new Vector();} else {r = new Pixel();}// 抽象层:图形(持有渲染器引用)Shape s = null;if ("Circle".equals(shapeType)) {s = new Circle(r);} else {s = new Rectangle(r);}s.draw();}}
}// 实现层接口:渲染器
interface Renderer {public String getName();
}class Vector implements Renderer {public String getName() {return "Vector Renderer";}
}class Pixel implements Renderer {public String getName() {return "Pixel Renderer";}
}// 抽象层:图形(持有渲染器引用)
abstract class Shape {protected Renderer renderer;protected String name;public Shape(String name, Renderer renderer) {this.name = name;this.renderer = renderer;}public void draw() {System.out.println("Drawing " + name + " with " + renderer.getName());}
}class Circle extends Shape {public Circle(Renderer r) {super("Circle", r);}
}class Rectangle extends Shape {public Rectangle(Renderer r) {super("Rectangle", r);}
}

扩展:实际项目中的桥接模式

JDBC 驱动架构

JDBC 是桥接模式最经典的应用。ConnectionStatement 等接口是抽象层,各数据库厂商的驱动(MySQL Connector、PostgreSQL Driver)是实现层。应用代码只依赖 JDBC 接口,不关心底层是哪个数据库——MySQL 升级驱动版本不影响业务代码,切换数据库只需换驱动和连接字符串。

消息通知系统

通知系统需要支持多种消息类型(普通、紧急、营销)和多种发送渠道(短信、邮件、微信)。用桥接模式,消息类型是抽象层,发送渠道是实现层。新增一种消息类型只需加一个 Message 子类,新增一种发送渠道只需加一个 MessageSender 实现,两者互不影响。

日志框架的 Appender

SLF4J + Logback 中,Logger 是抽象层,Appender(输出目标)是实现层。一个 Logger 可以配置多个 Appender,日志同时输出到控制台、文件、远程服务。新增异步 Logger 不影响 Appender,新增 Kafka Appender 不影响 Logger。

跨平台 UI 组件

UI 框架中,按钮是一个维度,操作系统是另一个维度。同一种按钮在不同操作系统上的渲染方式不同。用桥接模式,UI 组件是抽象层,操作系统渲染是实现层。新增下拉框不影响操作系统层,新增 Linux 支持不影响 UI 组件层。

支付方式与优惠策略

电商系统中,支付方式(支付宝、微信)是一个维度,优惠策略(无优惠、满减、折扣)是另一个维度。用桥接模式,订单是抽象层,支付方式是实现层。新增 Apple Pay 不影响优惠策略,新增"首单立减"不影响支付方式。

现在可能还在写简单的业务代码,但等到遇到"两个维度都要扩展"的场景时——与其让类数量爆炸,不如用桥接模式把它们拆开,各自独立发展。那时候就真的懂了。

技术交流 & 更多原创内容,关注公众号:咖啡八杯

http://www.gsyq.cn/news/1495649.html

相关文章:

  • 3步解锁iOS设备:告别iCloud激活锁的终极解决方案
  • NoFences:开源免费的Windows桌面分区管理利器
  • 2026上海接送阿姨家政公司口碑排行榜:六家专业靠谱服务品牌的个性化深度对比解析 - 企业推荐官【官方】
  • 2026电子站牌非标定制实力派排名:六家技术先锋厂商的核心定制优势与差异化设计深度解析 - 品牌发掘
  • 终极对比:Ji vs 其他Swift解析库,为什么它更适合你的项目?
  • 2026 年南京 GEO 优化五家服务商深度对比:本土技术力与落地实效测评 - 小艾信息发布
  • PowerToys中文版:让Windows操作效率翻倍的免费神器
  • i.MX50处理器I/O电气特性深度解析:从DC/AC参数到信号完整性设计
  • 阳台柜选购技术解析:从材质到定制全维度指南 - 起跑123
  • i.MX 6硬件设计核心:PLL时钟、I/O电气特性与系统时序深度解析
  • 计算机毕业设计之智能农产品推荐系统设计与实现
  • 2026 年宁波奉化室内除异味 / 新房除甲醛哪家好?垂直测评锁定宁波博豪环保 - 专注室内空气检测治理
  • 不锈钢榻榻米技术解析 靠谱厂家实测对比指南 - 起跑123
  • x265 加权预测(Weighted Prediction)深度剖析
  • 廊坊2026瓷砖空鼓翘边拱起原因及解决办法 免砸砖快速修复 - 苏易房屋修缮
  • 2026全屋净水品牌硬核实力榜:六家国民技术派企业核心优势与滤芯黑科技深度解析 - 品牌发掘
  • i.MX50 EIM与DRAM时序配置实战:从参数解析到稳定通信
  • 终极多AI协同指南:如何让ChatGPT、文心一言等10+智能助手同时为你工作
  • 3步将VR视频变普通:免费工具让你在电脑手机上看3D全景
  • 2026上海冷库公司技术实力榜单:六家领先餐饮冷链服务商的制冷创新与节能方案深度解析 - 品牌发掘
  • 别再手动巡检了!用vRealize Operations Manager自动生成虚拟化健康报告(附模板下载)
  • 嵌入式开发的终极简化:掌握Raspberry Pi Pico UF2格式固件更新技术
  • NVIDIA GeForce RTX 3080 魔改20G 运行大模型 - yi
  • 邮票纪念币正确保存方法!避开养护误区,留住藏品升值价值 - 深鉴新闻
  • Pytest接口自动化测试脚手架:YAML用例管理+MySQL断言+Allure报告+钉钉/企微通知
  • Google Cloud Messaging代码解析:深入理解Sender类和消息处理机制
  • 神经渲染:引爆下一代3D内容革命的AI引擎
  • SSHFS-Win深度解析:5种高效方案实现Windows与Linux跨平台文件同步
  • 从同步到异步:binance-java-api多模式编程详解
  • 亚马逊商品图片采集技术解析:变体图提取、高分辨率原图获取与多站点适配