news 2026/4/16 7:11:00

模板方法模式 (Template Method) 在支付系统中的应用:如何优雅地对接 10+ 个第三方支付通道?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
模板方法模式 (Template Method) 在支付系统中的应用:如何优雅地对接 10+ 个第三方支付通道?

💸 前言:被if-else支配的恐惧

场景还原:
老板:“小王,今天把微信支付接进来。”
小王:“好勒!” 于是写了一个pay()方法。
老板:“明天把支付宝也接进来。”
小王:“没问题!” 于是把pay()方法改成了:

if(channel==WECHAT){// 微信验签、调用、处理...}elseif(channel==ALIPAY){// 支付宝验签、调用、处理...}

老板:“下周我们要出海,接一下 PayPal 和 Stripe。”
小王看着那坨已经 500 行的if-else代码,陷入了沉思……

这就是典型的“缺乏抽象能力”。
支付业务虽然通道不同,但核心流程是惊人一致的

  1. 参数校验(Validate)
  2. 生成签名(Sign)
  3. 发送 HTTP 请求(Call API)
  4. 处理响应(Handle Response)
  5. 统一记录日志(Log)

针对这种**“流程固定,但细节不同”**的场景,模板方法模式就是唯一的真神。


🧠 核心原理:定义骨架,下放细节

模板方法模式的核心思想是:在父类中定义一个final的主流程方法(骨架),将具体的步骤延迟到子类去实现。

类图设计:

AbstractPaymentChannel
+pay(Request req) : void
#validate(Request req) : void
#sign(Request req) : String
#callApi(Request req, String sign) : Response
#handleResponse(Response resp) : void
-log() : void
WechatChannel
#validate()
#sign()
#callApi()
#handleResponse()
AlipayChannel
#validate()
#sign()
#callApi()
#handleResponse()

🛠️ 代码实战:重构支付网关

1. 定义抽象基类 (The Skeleton)

这是整个模式的灵魂。注意doPay方法必须是final的,防止子类篡改流程。

publicabstractclassAbstractPaymentChannel{// 核心模板方法:定义了支付的标准流程publicfinalPaymentResponsedoPay(PaymentRequestrequest){// 1. 通用日志logInfo("开始处理支付请求",request);// 2. 参数校验 (抽象步骤)if(!validate(request)){thrownewBizException("参数校验失败");}// 3. 加签 (抽象步骤)Stringsignature=sign(request);// 4. 调用三方接口 (抽象步骤)StringrawResponse=callApi(request,signature);// 5. 解析响应 (抽象步骤)PaymentResponseresponse=parseResponse(rawResponse);// 6. 通用后置处理postProcess(response);returnresponse;}// --- 抽象方法,强制子类实现 ---protectedabstractbooleanvalidate(PaymentRequestrequest);protectedabstractStringsign(PaymentRequestrequest);protectedabstractStringcallApi(PaymentRequestrequest,Stringsign);protectedabstractPaymentResponseparseResponse(StringrawResponse);// --- 通用方法,子类复用 ---privatevoidlogInfo(Stringmsg,Objectdata){// 统一的日志记录逻辑System.out.println(msg+": "+data);}// --- 钩子方法 (Hook),子类可选择性覆盖 ---protectedvoidpostProcess(PaymentResponseresponse){// 默认什么都不做}}
2. 实现微信支付通道 (Concrete Class)
@ServicepublicclassWechatPaymentChannelextendsAbstractPaymentChannel{@Overrideprotectedbooleanvalidate(PaymentRequestrequest){System.out.println("✅ 微信渠道:校验 OpenID 是否必填");returnStringUtils.hasText(request.getExtra("openId"));}@OverrideprotectedStringsign(PaymentRequestrequest){System.out.println("🔑 微信渠道:使用 MD5 进行签名");returnSecureUtil.md5(request.toString());}@OverrideprotectedStringcallApi(PaymentRequestrequest,Stringsign){System.out.println("🚀 微信渠道:调用 https://api.mch.weixin.qq.com/...");return"<xml>...SUCCESS...</xml>";}@OverrideprotectedPaymentResponseparseResponse(StringrawResponse){System.out.println("📝 微信渠道:解析 XML 响应");returnnewPaymentResponse("SUCCESS","200");}}
3. 实现支付宝通道 (Concrete Class)
@ServicepublicclassAlipayPaymentChannelextendsAbstractPaymentChannel{@Overrideprotectedbooleanvalidate(PaymentRequestrequest){System.out.println("✅ 支付宝渠道:校验 BuyerId");returntrue;}@OverrideprotectedStringsign(PaymentRequestrequest){System.out.println("🔑 支付宝渠道:使用 RSA2 进行签名");returnSecureUtil.rsa2(request.toString());}// ... 其他步骤实现}
4. 配合工厂模式使用

最后,我们需要一个简单工厂 (Simple Factory)或者策略模式 (Strategy)的 Map 来分发请求。

@ComponentpublicclassPaymentChannelFactory{@AutowiredprivateMap<String,AbstractPaymentChannel>channelMap;publicAbstractPaymentChannelgetChannel(StringchannelCode){// channelMap 会自动注入所有 Bean,key 为 beanName// 例如:wechatPaymentChannel -> WechatPaymentChannelreturnchannelMap.get(channelCode+"PaymentChannel");}}

💥 进阶技巧:钩子方法 (Hook) 的妙用

有时候,某个特定的渠道需要特殊的步骤。
比如:只有银联支付需要在支付完成后,发送短信通知用户。

我们不需要修改doPay主流程,只需要利用钩子方法

在基类中:

// 默认为空实现protectedvoidpostProcess(PaymentResponseresponse){}

在银联子类中:

@OverrideprotectedvoidpostProcess(PaymentResponseresponse){if("SUCCESS".equals(response.getStatus())){smsService.send("您的银联支付已成功!");}}

这就是“开闭原则” (Open-Closed Principle) 的完美体现:对扩展开放,对修改关闭。


📝 总结

if-else模板方法模式,不仅仅是代码行数的变化,更是思维方式的跃迁。

  1. 复用性:公共逻辑(日志、异常处理、埋点)全部收敛在父类,改一处,所有通道生效。
  2. 扩展性:接新通道?新建一个类继承父类即可,老代码一行都不用动,测试风险极低。
  3. 规范性:父类通过final关键字强制定义了业务的标准流程,新人想乱写都难。

写出机器能跑的代码是门槛,写出人能维护的代码才是本事。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 7:01:29

19、帧缓冲接口设计与图形库应用

帧缓冲接口设计与图形库应用 1. 帧缓冲的简单图形程序 我们可以通过编写一个简单的图形程序来展示 Linux 帧缓冲接口。这里的程序 hazy_moon 会显示一个直径为 240 像素的圆盘,其颜色从顶部的亮青色逐渐过渡到底部的朦胧红色。 1.1 程序运行步骤 程序的完整源代码位于 L…

作者头像 李华
网站建设 2026/4/12 15:02:54

20、帧缓冲接口设计与 STBmenu 简易 UI 构建

帧缓冲接口设计与 STBmenu 简易 UI 构建 1. 帧缓冲界面设计基础 1.1 SDL 绘制与事件处理 在进行帧缓冲界面设计时,SDL(Simple DirectMedia Layer)是一个常用的库。以下是一个简单的示例代码,用于在屏幕上显示 “Hello, world!” 消息,并处理键盘事件: SDL_Rect hell…

作者头像 李华
网站建设 2026/4/16 7:09:30

如何快速掌握Obsidian思维导图插件:新手友好的完整指南

如何快速掌握Obsidian思维导图插件&#xff1a;新手友好的完整指南 【免费下载链接】obsidian-enhancing-mindmap obsidian plugin editable mindmap,you can edit mindmap on markdown file 项目地址: https://gitcode.com/gh_mirrors/ob/obsidian-enhancing-mindmap 想…

作者头像 李华
网站建设 2026/4/16 7:01:51

23种语言零样本克隆!Chatterbox开源TTS打破商业模型垄断

23种语言零样本克隆&#xff01;Chatterbox开源TTS打破商业模型垄断 【免费下载链接】chatterbox 项目地址: https://ai.gitcode.com/hf_mirrors/ResembleAI/chatterbox 导语 你还在为AI语音千篇一律的机械腔调发愁&#xff1f;Resemble AI推出的Chatterbox开源TTS模型…

作者头像 李华
网站建设 2026/4/16 7:03:13

32、常见Shell及其相关特性介绍

常见Shell及其相关特性介绍 在操作系统中,Shell扮演着用户与系统内核之间的交互桥梁角色。不同的Shell具有各自独特的特性和功能,下面将为大家详细介绍几种常见的Shell。 1. POSIX标准与Shell POSIX标准的目的是促进Shell脚本的可移植性,它避免提及某些基本的实现问题。例…

作者头像 李华
网站建设 2026/4/10 15:16:07

沉浸式翻译终极故障排查指南:从失效到完美运行

当沉浸式翻译扩展突然出现异常&#xff0c;网页双语体验受到影响&#xff0c;你是否感到束手无策&#xff1f;本文为你整理完整的故障排查流程&#xff0c;从症状识别到根源解决&#xff0c;5分钟内恢复流畅翻译体验。 【免费下载链接】immersive-translate 沉浸式双语网页翻译…

作者头像 李华