Java 23 种设计模式:从踩坑到精通 | 番外:桥接模式 —— 配送服务 × 运输工具解耦实战

Java 23 种设计模式:从踩坑到精通 | 番外:桥接模式 —— 配送服务 × 运输工具解耦实战

摘要:桥接模式的核心思想是将抽象部分与实现部分解耦,使它们可以独立变化。本文结合**智能物流中“配送方式 × 运输工具”**的场景,完整展示如何用组合代替继承,避免 3×3=9 个类的爆炸问题。通过将配送服务(标准/加急/冷链)与运输工具(卡车/货轮/飞机)完全分离,你可以体会到桥接模式在应对多维度变化时的灵活与优雅。同时,本文还对桥接模式与策略模式进行了深度对比,帮你掌握“两个维度正交扩展”与“一个维度多种实现”的本质区别。

🗺️ 本文阅读地图(3 分钟速览)

  • 为什么配送方式 × 运输工具用继承会爆炸?
  • ✅ 桥接模式核心角色:抽象部分、实现部分、桥接关系
  • 手写标准/加急/冷链配送 + 卡车/货轮/飞机任意组合
  • 🚀 桥接 vs 策略:两个维度 vs 一个维度
  • 🎤 面试必问:“桥接模式和策略模式的区别是什么?”

📖 《Java 23 种设计模式:从踩坑到精通》
开篇:系列介绍与目录 | 正篇:桥接模式 Bridge —— 类爆炸?试试分离抽象与实现 | 当前:番外 · 桥接模式 × 配送服务与运输工具
🔗 返回系列总目录

1. 物流配送的多维度变化挑战

在物流平台中,配送服务存在两个独立变化的维度:配送方式(标准、加急、冷链)和运输工具(卡车、货轮、飞机)。每一种配送方式都可以选择不同的运输工具,例如:标准配送可用卡车或货轮,加急配送可用飞机或卡车,冷链配送可用卡车或飞机。

如果用继承来设计,需要为每种组合创建一个子类,例如 StandardTruckDeliveryStandardShipDeliveryExpressPlaneDelivery 等。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 是固定的模板方法,先计费再配送。子类只需要实现 delivercalculateCost,但可以使用任何注入的运输工具。

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定义抽象部分的接口,持有 ImplementorDeliveryService
RefinedAbstraction扩展抽象部分,实现具体业务StandardDeliveryExpressDeliveryColdChainDelivery
Implementor定义实现部分的接口Transport
ConcreteImplementor具体实现TruckCargoShipCargoPlane

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 种设计模式:从踩坑到精通》快速导航

🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。
📦 福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。

📌 除了设计模式,我也在深挖智能物流实战(WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》《电商多平台电子面单对接实战》。技术相通,思路可鉴。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值