跳到主要内容

桥接模式

桥接模式(Bridge Pattern)是一种结构型设计模式,其核心思想是将抽象部分与实现部分分离,使它们都可以独立地变化

模式概述

解决的问题

传统多层继承存在的问题:

  • 继承爆炸:类的数量呈指数级增长
  • 维护困难:继承层次过深,难以理解和维护
  • 违反原则:违背单一职责原则和开闭原则

核心思想

桥接模式通过组合关系替代继承关系,将两个独立变化的维度解耦,实现更灵活的系统设计。

关键洞察

桥接模式巧妙地将多层继承问题转换为组合问题,通过抽象关联来取代传统的多层继承,将类之间的静态继承关系变为动态的组合关系,有效控制了系统中类的个数,避免了继承层次的指数级爆炸。

模式结构

核心角色

  1. 抽象化角色(Abstraction)

    • 定义抽象接口
    • 包含对实现化对象的引用
    • 负责定义角色的行为
  2. 扩展抽象化角色(RefinedAbstraction)

    • 抽象化角色的子类
    • 实现具体的抽象行为
  3. 实现化角色(Implementor)

    • 定义实现接口
    • 供扩展抽象化角色调用
  4. 具体实现化角色(ConcreteImplementor)

    • 实现化角色的具体实现

关系特点

抽象化角色和实现化角色是组合关系,桥接模式最大的特点就是抽象角色引用实现角色。

模式原理

现实问题

蜡笔 vs 毛笔的对比

从图中可以看出:

  • 蜡笔:3 × 12 = 36支 - 需要36个类,不适合桥接模式
  • 毛笔:3 + 12(调色板) - 只需要15个类,适合桥接模式

蜡笔不适合桥接模式的原因:

  • 颜色是蜡笔的固有属性,无法分离
  • 型号和颜色是强耦合关系
  • 无法通过组合方式解耦

毛笔适合桥接模式的原因:

  • 毛笔本身不包含颜色,颜色通过外部调色板提供
  • 型号和颜色是两个独立变化的维度
  • 可以通过组合关系实现灵活的多维度变化

角色关系图

经典示例:毛笔系统

以毛笔系统为例说明桥接模式:

  • 抽象化角色:毛笔
  • 扩展抽象化角色:大号毛笔、中号毛笔、小号毛笔
  • 实现化角色:颜色接口
  • 具体实现化角色:黑色、红色、蓝色等

毛笔的型号和颜色是两个独立变化的维度,通过桥接模式实现了实体与行为的分离,将需要多层继承的场景转换为组合的方式。

设计本质

桥接模式的核心是对变化维度进行分类和抽象,通过组合关系实现灵活的多维度变化。

实际应用示例

支付系统场景

模拟不同的支付工具对应不同的支付模式:

  • 支付渠道:微信、支付宝
  • 支付方式:密码支付、人脸支付、指纹支付

传统方式的问题

package com.e6yun.project.structural.bridge;

import java.math.BigDecimal;

/**
* 支付控制器 - 传统方式
*/
public class PayController {

/**
* 支付功能
*
* @param uId 用户id
* @param tradeId 交易id
* @param amount 交易金额
* @param channelType 渠道类型 1 微信 2 支付宝
* @param modeType 支付类型 1 密码 2 人脸 3 指纹
* @return boolean
*/
public boolean doPay(String uId, String tradeId, BigDecimal amount, Integer channelType,
Integer modeType) {
// 微信支付
if (channelType == 1) {
System.out.println("微信渠道支付开始...");
if (1 == modeType) {
System.out.println("密码支付...");
} else if (2 == modeType) {
System.out.println("人脸支付...");
} else if (3 == modeType) {
System.out.println("指纹支付");
}
}
// 支付宝支付
if (channelType == 2) {
System.out.println("支付宝渠道支付开始...");
if (1 == modeType) {
System.out.println("密码支付...");
} else if (2 == modeType) {
System.out.println("人脸支付...");
} else if (3 == modeType) {
System.out.println("指纹支付");
}
}
return true;
}
}

传统方式的问题:

  • 代码重复严重
  • 难以扩展(新增渠道或支付方式需要修改多处)
  • 违反开闭原则
  • 维护成本高

桥接模式重构

核心思路:识别两个独立变化维度,设计两个独立的继承等级结构,建立抽象耦合。

1. 实现化角色(支付方式)

/**
* 支付方式接口 - 实现化角色
*/
public interface IPayMode {
boolean security(String uId);
}

/**
* 密码支付 - 具体实现化角色
*/
public class PayCypher implements IPayMode {
@Override
public boolean security(String uId) {
System.out.println("密码支付");
return true;
}
}

/**
* 人脸支付 - 具体实现化角色
*/
public class PayFaceMode implements IPayMode {
@Override
public boolean security(String uId) {
System.out.println("人脸支付");
return true;
}
}

/**
* 指纹支付 - 具体实现化角色
*/
public class PayFingerprintMode implements IPayMode {
@Override
public boolean security(String uId) {
System.out.println("指纹支付");
return true;
}
}

2. 抽象化角色(支付渠道)

/**
* 支付抽象类 - 抽象化角色
*/
public abstract class Pay {
protected IPayMode payMode;

public Pay(IPayMode payMode) {
this.payMode = payMode;
}

public abstract String transfer(String uId, String tradeId, BigDecimal amount);
}

/**
* 微信支付 - 扩展抽象化角色
*/
public class WxPay extends Pay {
public WxPay(IPayMode payMode) {
super(payMode);
}

@Override
public String transfer(String uId, String tradeId, BigDecimal amount) {
System.out.println("微信渠道支付");
boolean security = payMode.security(uId);
return "0000";
}
}

/**
* 支付宝支付 - 扩展抽象化角色
*/
public class ZfbPay extends Pay {
public ZfbPay(IPayMode payMode) {
super(payMode);
}

@Override
public String transfer(String uId, String tradeId, BigDecimal amount) {
System.out.println("支付宝渠道支付");
boolean security = payMode.security(uId);
return "0000";
}
}

3. 客户端使用

/**
* 桥接模式客户端
*/
public class PayController {

public boolean doPay(String uId, String tradeId, BigDecimal amount, Integer channelType,
Integer modeType) {
// 根据支付方式创建对应的实现
IPayMode payMode = null;
if (modeType == 1) {
payMode = new PayCypher();
} else if (modeType == 2) {
payMode = new PayFaceMode();
} else if (modeType == 3) {
payMode = new PayFingerprintMode();
}

// 根据渠道创建对应的支付对象
Pay pay = null;
if (channelType == 1) {
pay = new WxPay(payMode);
} else if (channelType == 2) {
pay = new ZfbPay(payMode);
}

return pay.transfer(uId, tradeId, amount) != null;
}
}

重构后的优势

  1. 解耦抽象与实现:支付渠道和支付方式可以独立变化
  2. 避免继承爆炸:传统方式需要 2×3=6 个类,桥接模式只需要 2+3=5 个类
  3. 易于扩展:新增渠道或支付方式不会影响现有代码
  4. 符合设计原则:开闭原则、单一职责原则、依赖倒置原则

模式总结

优点

  1. 解耦抽象与实现

    • 使用对象间的关联关系解耦抽象和实现
    • 抽象和实现可以沿着各自的维度独立变化
  2. 替代多层继承

    • 避免继承层次的指数级爆炸
    • 提高代码复用性和可维护性
  3. 提高系统扩展性

    • 在两个变化维度中任意扩展一个维度都不需要修改原有系统
    • 符合开闭原则
核心优势

桥接模式将类分为两个维度:实体维度(支付渠道)和功能维度(支付方式)。抽象化角色和实现化角色通过组合关系连接,实现了灵活的多维度变化。

缺点

  1. 增加设计复杂度

    • 要求开发者一开始就要对抽象层进行设计和编程
    • 增加了系统的理解和设计难度
  2. 需要丰富经验

    • 要求正确识别出系统中的两个独立变化维度
    • 维度的划分需要相当丰富的经验

使用场景

  1. 平台独立性应用

    • 不同数据库的 JDBC 驱动程序
    • 硬盘驱动程序
  2. 统一协议下的多组件

    • 支付场景中的各种支付渠道
    • 统一协议是收款、支付、扣款,组件是微信、支付宝等
  3. 消息驱动场景

    • 手机短信、邮件消息、QQ 消息、微信消息等
    • 消息行为统一(发送、接收、处理、回执),但具体实现各不相同
  4. 复杂类拆分

    • 当一个类中包含大量的对象和方法时
    • 既不方便阅读,也不方便修改
  5. 多维度扩展

    • 系统功能性和非功能性角度
    • 业务或技术角度等

与其他模式的区别

  • 与适配器模式:桥接模式是设计时考虑的结构,适配器模式是解决已有代码的兼容性问题
  • 与策略模式:桥接模式关注抽象与实现的分离,策略模式关注算法的选择
  • 与装饰器模式:桥接模式是静态的结构设计,装饰器模式是动态的功能扩展