Java 23 种设计模式:从踩坑到精通 | 番外:桥接模式 —— 配送服务 × 运输工具解耦实战
摘要:桥接模式的核心思想是将抽象部分与实现部分解耦,使它们可以独立变化。本文结合**智能物流中“配送方式 × 运输工具”**的场景,完整展示如何用组合代替继承,避免 3×3=9 个类的爆炸问题。通过将配送服务(标准/加急/冷链)与运输工具(卡车/货轮/飞机)完全分离,你可以体会到桥接模式在应对多维度变化时的灵活与优雅。同时,本文还对桥接模式与策略模式进行了深度对比,帮你掌握“两个维度正交扩展”与“一个维度多种实现”的本质区别。
🗺️ 本文阅读地图(3 分钟速览)
- 为什么配送方式 × 运输工具用继承会爆炸?
- ✅ 桥接模式核心角色:抽象部分、实现部分、桥接关系
- 手写标准/加急/冷链配送 + 卡车/货轮/飞机任意组合
- 🚀 桥接 vs 策略:两个维度 vs 一个维度
- 🎤 面试必问:“桥接模式和策略模式的区别是什么?”
📖 《Java 23 种设计模式:从踩坑到精通》
开篇:系列介绍与目录 | 正篇:桥接模式 Bridge —— 类爆炸?试试分离抽象与实现 | 当前:番外 · 桥接模式 × 配送服务与运输工具
🔗 返回系列总目录
1. 物流配送的多维度变化挑战
在物流平台中,配送服务存在两个独立变化的维度:配送方式(标准、加急、冷链)和运输工具(卡车、货轮、飞机)。每一种配送方式都可以选择不同的运输工具,例如:标准配送可用卡车或货轮,加急配送可用飞机或卡车,冷链配送可用卡车或飞机。
如果用继承来设计,需要为每种组合创建一个子类,例如 StandardTruckDelivery、StandardShipDelivery、ExpressPlaneDelivery 等。3 种配送方式 × 3 种运输工具 = 9 个类。如果未来增加一种配送方式(如同城闪送)或一种运输工具(如高铁货运),类的数量会进一步爆炸。而且,运输工具的行为(装载、卸载、载重限制)被绑定在配送子类中,无法独立复用。
桥接模式正是为这种“多维度变化”场景而生的:它将抽象部分(配送方式)与实现部分(运输工具)分离,让两者可以独立扩展,只需用组合连接,类数量从 M × N 降为 M + N。
1.1 你的场景该不该用桥接模式?
| 判断标准 | 是 → 用桥接模式 | 否 → 用其他方式 |
|---|---|---|
| 存在两个及以上独立变化的维度 | ✅ | ❌ |
| 希望两个维度可以独立扩展,互不影响 | ✅ | ❌ |
| 需要运行时动态组合不同维度的实现 | ✅ | ❌ |
| 只有一个变化维度 | ❌ | 用普通继承或策略模式 |
2. 桥接模式 UML(配送方式 × 运输工具)

3. 完整源码实现
3.1 实现部分接口:运输工具 (Transport)
/**
* 实现部分接口:运输工具
* 定义所有运输方式的统一行为
*/
public interface Transport {
void loadCargo(String cargo, int weight);
void unloadCargo();
int getMaxWeight();
String getTransportType();
int getCurrentWeight();
}
💬 白话:运输工具维度有自己的接口,定义了装载、卸载、载重上限等通用行为。无论是卡车、货轮还是飞机,都要实现这些方法,但内部逻辑各不相同。
3.2 具体实现A:卡车 (Truck)
/**
* 具体实现A:卡车运输
* 适合中短途陆运,灵活性强
*/
public class Truck implements Transport {
private int currentWeight = 0;
private static final int MAX_WEIGHT = 20000; // 20吨
@Override
public void loadCargo(String cargo, int weight) {
if (currentWeight + weight <= MAX_WEIGHT) {
currentWeight += weight;
System.out.printf("🚛 卡车装载:[%s] %.1f吨\n", cargo, weight / 1000.0);
} else {
System.out.printf("❌ 卡车超载!当前%.1f吨,请求%.1f吨,上限%.1f吨\n",
currentWeight / 1000.0, weight / 1000.0, MAX_WEIGHT / 1000.0);
}
}
@Override
public void unloadCargo() {
System.out.printf("🚛 卡车已卸载 %.1f吨货物\n", currentWeight / 1000.0);
currentWeight = 0;
}
@Override
public int getMaxWeight() { return MAX_WEIGHT; }
@Override
public String getTransportType() { return "陆运卡车"; }
@Override
public int getCurrentWeight() { return currentWeight; }
}
💬 白话:卡车最大载重 20 吨,装载时会检查是否超载。它实现了
Transport接口,但内部的载重限制和日志输出完全是自己的逻辑。
3.3 具体实现B:货轮 (CargoShip)
/**
* 具体实现B:货轮运输
* 适合远洋/内河运输,载重大成本低
*/
public class CargoShip implements Transport {
private int currentWeight = 0;
private static final int MAX_WEIGHT = 50000000; // 5万吨
@Override
public void loadCargo(String cargo, int weight) {
if (currentWeight + weight <= MAX_WEIGHT) {
currentWeight += weight;
System.out.printf("🚢 货轮装载:[%s] %.1f吨\n", cargo, weight / 1000.0);
} else {
System.out.printf("❌ 货轮超载!当前%.1f吨,请求%.1f吨,上限%.1f吨\n",
currentWeight / 1000.0, weight / 1000.0, MAX_WEIGHT / 1000.0);
}
}
@Override
public void unloadCargo() {
System.out.printf("🚢 货轮已卸载 %.1f吨货物\n", currentWeight / 1000.0);
currentWeight = 0;
}
@Override
public int getMaxWeight() { return MAX_WEIGHT; }
@Override
public String getTransportType() { return "海运货轮"; }
@Override
public int getCurrentWeight() { return currentWeight; }
}
3.4 具体实现C:货运飞机 (CargoPlane)
/**
* 具体实现C:航空运输
* 适合紧急/高价值货物,速度快成本高
*/
public class CargoPlane implements Transport {
private int currentWeight = 0;
private static final int MAX_WEIGHT = 100000; // 100吨
@Override
public void loadCargo(String cargo, int weight) {
if (currentWeight + weight <= MAX_WEIGHT) {
currentWeight += weight;
System.out.printf("✈️ 货机装载:[%s] %.1f吨\n", cargo, weight / 1000.0);
} else {
System.out.printf("❌ 货机超载!当前%.1f吨,请求%.1f吨,上限%.1f吨\n",
currentWeight / 1000.0, weight / 1000.0, MAX_WEIGHT / 1000.0);
}
}
@Override
public void unloadCargo() {
System.out.printf("✈️ 货机已卸载 %.1f吨货物\n", currentWeight / 1000.0);
currentWeight = 0;
}
@Override
public int getMaxWeight() { return MAX_WEIGHT; }
@Override
public String getTransportType() { return "航空货运"; }
@Override
public int getCurrentWeight() { return currentWeight; }
}
3.5 抽象部分:配送服务 (DeliveryService)
/**
* 抽象部分:配送服务
* 持有一个运输工具的引用,形成“桥接”
*/
public abstract class DeliveryService {
protected Transport transport;
protected String deliveryId;
protected String cargoName;
protected int cargoWeight;
public DeliveryService(Transport transport, String deliveryId) {
this.transport = transport;
this.deliveryId = deliveryId;
}
/**
* 执行配送(模板方法)
*/
public final void executeDelivery(String cargo, int weight) {
System.out.println("\n========== 配送单 [" + deliveryId + "] ==========");
calculateCost();
deliver(cargo, weight);
System.out.println("✅ 配送单 [" + deliveryId + "] 已完成\n");
}
public abstract void deliver(String cargo, int weight);
public abstract void calculateCost();
public void track() {
System.out.printf("📍 配送单[%s] 当前状态:运输中 | 工具:%s | 载重:%.1f吨\n",
deliveryId, transport.getTransportType(), transport.getCurrentWeight() / 1000.0);
}
}
💬 白话:
DeliveryService是抽象部分,它持有一个Transport引用,这就是“桥”。executeDelivery是固定的模板方法,先计费再配送。子类只需要实现deliver和calculateCost,但可以使用任何注入的运输工具。
3.6 扩展抽象A:标准配送 (StandardDelivery)
public class StandardDelivery extends DeliveryService {
private static final double BASE_RATE = 2.0; // 每公斤2元
public StandardDelivery(Transport transport, String deliveryId) {
super(transport, deliveryId);
}
@Override
public void deliver(String cargo, int weight) {
this.cargoName = cargo;
this.cargoWeight = weight;
transport.loadCargo(cargo, weight);
System.out.printf("📦 标准配送 [%s] 已发出,预计3-5天送达\n", cargo);
transport.unloadCargo();
}
@Override
public void calculateCost() {
double cost = cargoWeight * BASE_RATE;
System.out.printf("💰 标准配送计费:%.1f公斤 × ¥%.1f/公斤 = ¥%.2f\n",
cargoWeight / 1000.0, BASE_RATE, cost);
}
}
3.7 扩展抽象B:加急配送 (ExpressDelivery)
public class ExpressDelivery extends DeliveryService {
private static final double BASE_RATE = 2.0;
private static final double EXPRESS_MULTIPLIER = 2.5;
public ExpressDelivery(Transport transport, String deliveryId) {
super(transport, deliveryId);
}
@Override
public void deliver(String cargo, int weight) {
this.cargoName = cargo;
this.cargoWeight = weight;
transport.loadCargo(cargo, weight);
System.out.printf("🚀 加急配送 [%s] 已优先发出,预计次日达\n", cargo);
transport.unloadCargo();
}
@Override
public void calculateCost() {
double cost = cargoWeight * BASE_RATE * EXPRESS_MULTIPLIER;
System.out.printf("💰 加急配送计费:%.1f公斤 × ¥%.1f/公斤 × %.1f倍 = ¥%.2f\n",
cargoWeight / 1000.0, BASE_RATE, EXPRESS_MULTIPLIER, cost);
}
}
3.8 扩展抽象C:冷链配送 (ColdChainDelivery)
public class ColdChainDelivery extends DeliveryService {
private static final double BASE_RATE = 2.0;
private static final double COLD_CHAIN_FEE = 500.0;
private double temperature;
public ColdChainDelivery(Transport transport, String deliveryId, double temperature) {
super(transport, deliveryId);
this.temperature = temperature;
}
@Override
public void deliver(String cargo, int weight) {
this.cargoName = cargo;
this.cargoWeight = weight;
System.out.printf("🧊 冷链配送 [%s] 温控设定:%.1f°C\n", cargo, temperature);
transport.loadCargo(cargo, weight);
System.out.printf("❄️ 冷链运输中,全程温度监控...\n");
transport.unloadCargo();
}
@Override
public void calculateCost() {
double cost = cargoWeight * BASE_RATE + COLD_CHAIN_FEE;
System.out.printf("💰 冷链配送计费:%.1f公斤 × ¥%.1f/公斤 + 制冷费¥%.1f = ¥%.2f\n",
cargoWeight / 1000.0, BASE_RATE, COLD_CHAIN_FEE, cost);
}
}
3.9 客户端测试
public class Client {
public static void main(String[] args) {
System.out.println("=== 智能物流配送系统(桥接模式)===\n");
// 场景1:标准配送 + 卡车
System.out.println("【场景1】标准配送 + 卡车运输");
System.out.println("=".repeat(60));
Transport truck = new Truck();
DeliveryService standardByTruck = new StandardDelivery(truck, "STD-001");
standardByTruck.executeDelivery("电子产品", 5000);
standardByTruck.track();
// 场景2:加急配送 + 货机
System.out.println("\n" + "=".repeat(60));
System.out.println("【场景2】加急配送 + 航空运输");
System.out.println("=".repeat(60));
Transport plane = new CargoPlane();
DeliveryService expressByPlane = new ExpressDelivery(plane, "EXP-001");
expressByPlane.executeDelivery("医疗急救物资", 2000);
expressByPlane.track();
// 场景3:冷链配送 + 卡车
System.out.println("\n" + "=".repeat(60));
System.out.println("【场景3】冷链配送 + 卡车运输");
System.out.println("=".repeat(60));
Transport truck2 = new Truck();
DeliveryService coldChainByTruck = new ColdChainDelivery(truck2, "COLD-001", -18.0);
coldChainByTruck.executeDelivery("冷冻海鲜", 3000);
coldChainByTruck.track();
// 场景4:标准配送 + 货轮
System.out.println("\n" + "=".repeat(60));
System.out.println("【场景4】标准配送 + 海运货轮");
System.out.println("=".repeat(60));
Transport ship = new CargoShip();
DeliveryService standardByShip = new StandardDelivery(ship, "STD-002");
standardByShip.executeDelivery("机械设备", 1000000);
standardByShip.track();
// 场景5:展示桥接的灵活性
System.out.println("\n" + "=".repeat(60));
System.out.println("【场景5】桥接的灵活性演示");
System.out.println("=".repeat(60));
System.out.println("同一份加急订单,可以灵活选择不同运输工具:");
DeliveryService express1 = new ExpressDelivery(new Truck(), "EXP-FLEX-01");
System.out.print("方案A(卡车): ");
express1.executeDelivery("文件", 100);
DeliveryService express2 = new ExpressDelivery(new CargoPlane(), "EXP-FLEX-02");
System.out.print("方案B(飞机): ");
express2.executeDelivery("文件", 100);
}
}
4. 运行结果
=== 智能物流配送系统(桥接模式)===
【场景1】标准配送 + 卡车运输
============================================================
💰 标准配送计费:5.0公斤 × ¥2.0/公斤 = ¥10000.00
🚛 卡车装载:[电子产品] 5.0吨
📦 标准配送 [电子产品] 已发出,预计3-5天送达
🚛 卡车已卸载 5.0吨货物
✅ 配送单 [STD-001] 已完成
📍 配送单[STD-001] 当前状态:运输中 | 工具:陆运卡车 | 载重:0.0吨
============================================================
【场景2】加急配送 + 航空运输
============================================================
💰 加急配送计费:2.0公斤 × ¥2.0/公斤 × 2.5倍 = ¥10000.00
✈️ 货机装载:[医疗急救物资] 2.0吨
🚀 加急配送 [医疗急救物资] 已优先发出,预计次日达
✈️ 货机已卸载 2.0吨货物
✅ 配送单 [EXP-001] 已完成
📍 配送单[EXP-001] 当前状态:运输中 | 工具:航空货运 | 载重:0.0吨
============================================================
【场景3】冷链配送 + 卡车运输
============================================================
💰 冷链配送计费:3.0公斤 × ¥2.0/公斤 + 制冷费¥500.0 = ¥6500.00
🧊 冷链配送 [冷冻海鲜] 温控设定:-18.0°C
🚛 卡车装载:[冷冻海鲜] 3.0吨
❄️ 冷链运输中,全程温度监控...
🚛 卡车已卸载 3.0吨货物
✅ 配送单 [COLD-001] 已完成
📍 配送单[COLD-001] 当前状态:运输中 | 工具:陆运卡车 | 载重:0.0吨
============================================================
【场景4】标准配送 + 海运货轮
============================================================
💰 标准配送计费:1000.0公斤 × ¥2.0/公斤 = ¥2000000.00
🚢 货轮装载:[机械设备] 1000.0吨
📦 标准配送 [机械设备] 已发出,预计3-5天送达
🚢 货轮已卸载 1000.0吨货物
✅ 配送单 [STD-002] 已完成
📍 配送单[STD-002] 当前状态:运输中 | 工具:海运货轮 | 载重:0.0吨
============================================================
【场景5】桥接的灵活性演示
============================================================
同一份加急订单,可以灵活选择不同运输工具:
方案A(卡车):
========== 配送单 [EXP-FLEX-01] ==========
💰 加急配送计费:0.1公斤 × ¥2.0/公斤 × 2.5倍 = ¥500.00
🚛 卡车装载:[文件] 0.1吨
🚀 加急配送 [文件] 已优先发出,预计次日达
🚛 卡车已卸载 0.1吨货物
✅ 配送单 [EXP-FLEX-01] 已完成
方案B(飞机):
========== 配送单 [EXP-FLEX-02] ==========
💰 加急配送计费:0.1公斤 × ¥2.0/公斤 × 2.5倍 = ¥500.00
✈️ 货机装载:[文件] 0.1吨
🚀 加急配送 [文件] 已优先发出,预计次日达
✈️ 货机已卸载 0.1吨货物
✅ 配送单 [EXP-FLEX-02] 已完成
5. 核心角色回顾
| 角色 | 职责 | 对应代码 |
|---|---|---|
| Abstraction | 定义抽象部分的接口,持有 Implementor | DeliveryService |
| RefinedAbstraction | 扩展抽象部分,实现具体业务 | StandardDelivery、ExpressDelivery、ColdChainDelivery |
| Implementor | 定义实现部分的接口 | Transport |
| ConcreteImplementor | 具体实现 | Truck、CargoShip、CargoPlane |
6. 桥接模式 vs 策略模式
| 对比项 | 桥接模式 | 策略模式 |
|---|---|---|
| 意图 | 解耦两个独立变化的维度 | 封装一组可互换的算法 |
| 结构 | 抽象类持有实现接口,两边都可扩展 | Context 持有 Strategy 接口 |
| 关注点 | 关注“是什么”(What)— 结构层面 | 关注“怎么做”(How)— 行为层面 |
| 典型应用 | JDBC DriverManager + Driver、跨平台 UI | 排序算法、支付方式、促销策略 |
💡 一句话记忆:桥接模式是“两个维度正交扩展”,策略模式是“一个维度多种实现”。本例中配送服务和运输工具是两个独立变化的维度,所以用桥接;如果只有“计费方式”一个维度有多种算法,就用策略模式。
7. 桥接模式的优缺点
| 优点 | 缺点 |
|---|---|
| 彻底解决多维度类爆炸,M+N 替代 M×N | 增加系统设计复杂度 |
| 抽象与实现解耦,可独立扩展 | 需要识别出两个独立变化维度 |
| 运行时动态组合不同实现 | 单维度变化时过度设计 |
8. 六大设计原则体现
| 原则 | 体现 |
|---|---|
| 单一职责 | 运输工具只负责运输,配送服务只负责业务流程 |
| 开闭原则 | 新增配送方式或运输工具无需修改现有代码 |
| 里氏替换 | 所有运输工具可替换 Transport 接口 |
| 依赖倒置 | DeliveryService 依赖 Transport 抽象 |
| 接口隔离 | Transport 接口只有运输相关方法 |
| 迪米特法则 | 客户端只与 DeliveryService 交互 |
附 桥接模式 UML源码(配送方式 × 运输工具)
@startuml
title Java 23 种设计模式:从踩坑到精通
footer 折哥 | 智能物流与Java实战
' 1. 全局样式配置
skinparam backgroundColor #FEFEFE
skinparam shadowing false
skinparam classBorderColor #333333
skinparam classFontColor #1A1A1A
skinparam classFontSize 14
skinparam noteFontSize 12
skinparam noteFontColor #555555
skinparam arrowColor #555555
skinparam classBackgroundColor #F9F9F9
' 2. 实现部分:运输工具接口
interface Transport {
+ void loadCargo(String cargo, int weight)
+ void unloadCargo()
+ int getMaxWeight()
+ String getTransportType()
}
note right of Transport
<b>实现部分接口(运输工具)</b>
--
定义所有运输工具的统一行为
装载、卸载、载重上限、类型标识
end note
' 3. 具体实现:卡车
class Truck implements Transport {
- currentWeight : int
+ void loadCargo(String cargo, int weight)
+ void unloadCargo()
+ int getMaxWeight()
+ String getTransportType()
}
note right of Truck
<b>具体实现A(卡车运输)</b>
--
适合中短途陆运
载重10-30吨,灵活性强
end note
' 4. 具体实现:货轮
class CargoShip implements Transport {
- currentWeight : int
+ void loadCargo(String cargo, int weight)
+ void unloadCargo()
+ int getMaxWeight()
+ String getTransportType()
}
note right of CargoShip
<b>具体实现B(货轮运输)</b>
--
适合远洋/内河运输
载重万吨级,成本低但速度慢
end note
' 5. 具体实现:货运飞机
class CargoPlane implements Transport {
- currentWeight : int
+ void loadCargo(String cargo, int weight)
+ void unloadCargo()
+ int getMaxWeight()
+ String getTransportType()
}
note right of CargoPlane
<b>具体实现C(航空运输)</b>
--
适合紧急/高价值货物
速度快但成本极高
end note
' 6. 抽象部分:配送服务
abstract class DeliveryService {
- transport : Transport
- deliveryId : String
+ DeliveryService(Transport transport, String deliveryId)
+ abstract void deliver(String cargo, int weight)
+ abstract void calculateCost()
+ void track()
}
note right of DeliveryService
<b>抽象部分(配送服务)</b>
--
定义配送的抽象行为
持有一个运输工具的实现
抽象与实现通过"桥"连接
end note
' 7. 扩展抽象:标准配送
class StandardDelivery extends DeliveryService {
+ void deliver(String cargo, int weight)
+ void calculateCost()
}
note right of StandardDelivery
<b>扩展抽象A(标准配送)</b>
--
普通时效,经济实惠
成本 = 重量 × 基础费率
end note
' 8. 扩展抽象:加急配送
class ExpressDelivery extends DeliveryService {
+ void deliver(String cargo, int weight)
+ void calculateCost()
}
note right of ExpressDelivery
<b>扩展抽象B(加急配送)</b>
--
优先处理,时效保障
成本 = 标准费用 × 2.5倍加急系数
end note
' 9. 扩展抽象:冷链配送
class ColdChainDelivery extends DeliveryService {
- temperature : double
+ void deliver(String cargo, int weight)
+ void calculateCost()
}
note right of ColdChainDelivery
<b>扩展抽象C(冷链配送)</b>
--
温控运输,全程冷链
成本 = 标准费用 + 制冷设备费
end note
' 10. 关系连线
DeliveryService o--> Transport : 桥接/使用
DeliveryService <|-- StandardDelivery : 继承
DeliveryService <|-- ExpressDelivery : 继承
DeliveryService <|-- ColdChainDelivery : 继承
Transport <|-- Truck : 实现
Transport <|-- CargoShip : 实现
Transport <|-- CargoPlane : 实现
@enduml
🧭 《Java 23 种设计模式:从踩坑到精通》快速导航
- 开篇:系列介绍与目录
- 正篇:桥接模式 Bridge —— 类爆炸?试试分离抽象与实现
- 当前:番外 · 桥接模式 × 配送服务与运输工具(你在这里)
- 创建型模式汇总
- 结构型模式汇总
- 行为型模式汇总
🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。
📦 福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。
📌 除了设计模式,我也在深挖智能物流实战(WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》、《电商多平台电子面单对接实战》。技术相通,思路可鉴。

39

被折叠的 条评论
为什么被折叠?



