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

你把支付渠道写在 if-else 里——桥接模式早就把实现和抽象拆开了

每个多渠道支付系统都以同样的方式开始:一个 PaymentService 加一个巨大的 switch 语句。支付宝?调 alipayClient.pay()。微信?调 wechatClient.pay()。银联?调 unionPayClient.pay()。银行转账?那完全是另一种流程。每个渠道有自己的请求格式、自己的错误处理、自己的幂等机制。

代码长成怪物。不是因为业务逻辑复杂——每次都是三步(创建订单、调渠道、处理结果)。复杂来自耦合:每次加渠道你改 PaymentService。每次渠道改 API 你改 PaymentService。每次加功能(日志、重试、回调)你改每个 if-else 分支。

桥接模式把系统拆成两个层级:抽象层级(你做什么)和实现层级(你怎么做)。抽象层级定义支付流程。实现层级定义渠道特定行为。它们独立演化。加渠道意味着在实现层级加一个类。加功能意味着修改抽象层级。任一变更不触碰另一个。

if-else 支付怪兽

这是大多数支付服务几年后的样子:

```java @Service public class PaymentService {

public PaymentResult pay(PaymentRequest request) { String channel = request.getChannel(); if ("alipay".equals(channel)) { AlipayRequest alipayReq = new AlipayRequest(); alipayReq.setOutTradeNo(request.getOrderId()); alipayReq.setTotalAmount(request.getAmount().toString()); AlipayResponse resp = alipayClient.tradePay(alipayReq); if (resp.isSuccess()) { return PaymentResult.success(resp.getTradeNo()); } else { return PaymentResult.fail(resp.getSubCode(), resp.getSubMsg()); } } else if ("wechat".equals(channel)) { WechatPayRequest wechatReq = new WechatPayRequest(); WechatPayResponse resp = wechatClient.pay(wechatReq); } else if ("unionpay".equals(channel)) { // 银联完全不同 } throw new UnsupportedChannelException(channel); }

} ```

问题不是 if-else 本身——而是每个分支包含完全不同的代码。请求构建、API 调用、错误处理、幂等——全部因渠道而异,全部活在同一个方法里。

这是最糟糕的耦合:抽象(支付流程)和实现(渠道细节)融合在一起。桥接模式把它们分开。

桥接模式:两个层级的解法

桥接模式定义两个独立层级,通过一个引用连接:

``` Abstraction (PaymentProcess) ├── RefinedAbstraction (StandardPaymentProcess) ├── RefinedAbstraction (RetryablePaymentProcess)

Implementation (PaymentChannel) ├── ConcreteImpl (AlipayChannel) ├── ConcreteImpl (WechatChannel) ├── ConcreteImpl (UnionPayChannel) ```

```java // 实现层级:渠道特定行为 public interface PaymentChannel { ChannelRequest buildRequest(PaymentOrder order); ChannelResponse callChannel(ChannelRequest request); PaymentResult parseResponse(ChannelResponse response); boolean supports(String channelCode); }

// 抽象层级:支付流程 public abstract class PaymentProcess { protected PaymentChannel channel;

public PaymentProcess(PaymentChannel channel) { this.channel = channel; } public PaymentResult process(PaymentOrder order) { ChannelRequest request = channel.buildRequest(order); ChannelResponse response = channel.callChannel(request); PaymentResult result = channel.parseResponse(response); return result; }

}

// 细化抽象:加重试 public class RetryablePaymentProcess extends PaymentProcess { private final int maxRetries;

@Override public PaymentResult process(PaymentOrder order) { for (int i = 0; i <= maxRetries; i++) { PaymentResult result = super.process(order); if (result.isSuccess() || !result.isRetryable()) { return result; } } return PaymentResult.fail("MAX_RETRIES_EXCEEDED"); }

} ```

两个层级独立演化:加渠道不改 PaymentProcess,加重试不改 PaymentChannel,改支付宝 API 不改其他渠道。

JDBC:桥接模式的教科书案例

JDBC 是桥接模式最纯粹的形式:

```java Connection conn = DriverManager.getConnection(url); PreparedStatement ps = conn.prepareStatement("SELECT * FROM users"); ResultSet rs = ps.executeQuery();

// 实现:MySQL ConnectionImpl / PostgreSQL PgConnection / OracleConnection ```

java.sql.Connection 是抽象,定义数据库操作接口。MySQL/PostgreSQL/Oracle 的驱动是实现,处理各自特定协议。你换数据库就换驱动(实现层级)。你换 JDBC 功能就升级 JDK(抽象层级)。任一变更不碰另一个。

Spring AbstractRoutingDataSource:桥接 + 策略

Spring 的 AbstractRoutingDataSource 是桥接结合策略做实现选择:

```java public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {

private Map<Object, DataSource> resolvedDataSources; @Override public Connection getConnection() throws SQLException { return determineTargetDataSource().getConnection(); } protected DataSource determineTargetDataSource() { Object lookupKey = determineCurrentLookupKey(); DataSource ds = resolvedDataSources.get(lookupKey); return ds != null ? ds : resolvedDefaultDataSource; } protected abstract Object determineCurrentLookupKey();

} ```

桥接引用不是静态的——基于 Context 动态选择。抽象控制流程,委托给 Context 要求的实现。

桥接什么时候比策略更合适

策略模式:实现每次调用都变,基于当前 Context 选策略,策略是可互换替代品。

java PaymentStrategy strategy = selector.select(order); strategy.pay(order);

桥接模式:实现是稳定的变异维度,抽象和实现都是独立演化的层级,桥接引用设置一次不随调用变化。

java PaymentProcess process = new RetryablePaymentProcess(new AlipayChannel()); process.process(order1); process.process(order2); // 同渠道,不同订单

支付系统通常是桥接不是策略,因为抽象有变体(标准、可重试、异步),实现有变体(支付宝、微信、银联),两个维度独立演化。

桥接模式的工程陷阱

陷阱1:实现接口膨胀

实现接口长到包含两个层级需要的所有功能,变成上帝接口。修复:拆成核心 + 可选能力(RetryableChannel, AsyncChannel),抽象使用前检查 instanceof。

陷阱2:桥接引用泄漏

外部代码直接访问实现,抽象对流程的控制就丢了。修复:不暴露实现引用,给抽象加委托方法。

陷阱3:实现选择硬编码

如果抽象总是创建同一个实现,桥接就没意义。这是伪装成桥接的策略。选择应该发生在抽象外面——通过注入、配置或工厂。

桥接不是策略的别名

桥接是两个层级的独立演化。策略是每次调用的实现选择。支付渠道、数据库驱动、日志后端、消息协议——这些都是桥接候选,因为抽象和实现都是真实层级,有自己的演化时间线。

如果你一直叫这些"策略"用 Map 查找来建它们,你用了错误的模式。Map 是扁平选择器。桥接需要两个层级。你的系统有它们——你只是从来没给它们分开的接口。

对了,我在做的那个小程序「爪爪代码冒险记」里,桥接模式用卡皮巴拉走两条吊桥过河来类比——抽象桥和实现桥各自延伸,中间对接。比纯看代码直观,搜搜看。

http://www.jsqmd.com/news/1054181/

相关文章:

  • 遵义市黄金回收猫腻多怎么办?整理了5家诚信回收店供参考 - 奢金汇
  • 一站式解决九大网盘下载难题:LinkSwift直链下载助手终极指南
  • 国产大模型合规接入与私有化部署指南
  • 百度网盘直链解析:5分钟解锁高速下载的完整教程
  • 基于FreeMASTER与MCAT的PMSM电机FOC参数整定实战指南
  • DeepSeek官网访问与本地化调用实战指南
  • 沧州市黄金回收多少钱一克?本地实体门店回收价格对比整理 - 开始就结束
  • 北京翡翠回收 2026 经验谈:西城区实体老店专业鉴品,定价贴合市场主流行情 - 薛定谔的梨花猫
  • 汉中市今日黄金回收价格多少?本地5家口碑门店报价参考 - 奢金汇
  • 还在为运动步数烦恼?这款智能工具让你轻松管理每日健康数据
  • 今天我的朋友们都出去玩了!
  • 2026年6月最新万国中国官方售后服务网点地址及客服电话一览 - 亨得利官方服务中心
  • 桌面歌词神器LyricsX:让你的Mac音乐体验沉浸式升级
  • 2026年最新天津律师测评,资深专家律师婚姻修复/财产保护子女权益 - 资讯速览
  • 扩散模型推理能效优化:从U-Net架构改进到热力学视角的实践指南
  • 3分钟搞定Unity游戏汉化:XUnity自动翻译器让外语游戏变中文
  • ★银座购物卡回收靠谱吗?山东大学生异地盘活福利实测 - 京顺回收
  • 2026肇庆黄金回收实用手册:价格走势与六家正规门店评测 - 余生黄金回收
  • Rocky Linux 9安装Node.js:nvm与NodeSource选型指南
  • 琼中黎族苗族自治县2026年黄金回收报价,内行人整理实体门店回收清单 - 奢金阁
  • 2026江阴装修设计落地难?红豆香江豪庭业主:还原度95%以上,这钱花得值 - 装企自媒体训练营辉哥
  • 嵌入式GUI开发实战:emWin 2D绘图与图像显示API详解
  • NFC Cube开发套件实战:从硬件解析到NDEF应用开发
  • 2026杭州防水补漏避坑指南:卫生间/厨房/阳台/屋顶/地下室漏水检测维修全攻略,正规施工+透明报价+口碑榜靠谱服务商推荐 - 安佳防水
  • Gemini 3.5 Flash实操指南:结构化输入三法则提升准确率至96%+
  • 2026智能电话外呼机器人权威测评 全能型品牌TOP6企业选型指南 - GrowthUME
  • 供应商管理系统厂商实施能力:历史项目平均上线周期、二次开发响应时效及培训体系 - 品牌排行榜
  • 2026常德本地正规瓷砖空鼓维修服务商盘点|无损免拆砖修复,全域上门售后有保障 - 宅安选房屋修缮
  • 和田地区闲置黄金变现多少钱?本地5家回收门店最新报价参考 - 奢金汇
  • 2026六安本地正规瓷砖空鼓维修服务商盘点|无损免拆砖修复,全域上门售后有保障 - 宅安选房屋修缮