今天这篇文章所涉及的相关观点做法,按照我们公司某位架构师的说法就是,架构这东西没有对错之分,集思广益,不一定我的做法就是对的,不一定我的做法就是最优的,小编和大家一起探讨探讨。
一、场景
首先我们模拟一个SDK交易链路的场景,对交易不是很懂的同学可以先移步至微信支付了解下大概业务,其实小编也不是特别精通交易,刚实习,但这不影响本文哈!一笔交易的生命周期大致可以分为这么几个阶段,即预下单---支付---交易通知,什么意思呢?拿微信扫码点餐付款来说,首先微信客户端扫码之后会先生成一笔订单,当预下单完成之后,客户端会触发SDK发起支付请求,然后用户输入密码完成支付,支付之后会发送订单状态通知用户,我们最后才能知道我到底付款成功没有。
下面是微信扫码支付业务时序图:
既然业务步骤是明确的,我们首先考虑代码方面应该怎么去架设会好一点,首先猜想能否定义三个生命周期方法,如下:
// 前置业务---预下单 beforeService() // 核心业务处理----支付 doService() // 后置业务处理----通知 afterService() |
但是,实际业务每个步骤往往非常复杂,也就说比如我们有可能在预下单的时候会加入优惠信息,判断当前用户这笔订单是否符合优惠,如果优惠后的金额为0,还有可能不需要任何的支付处理,直接就发送通知给到用户。还有一点我们业务通常还要区别是刷卡预下单还是扫码预下单,扫码预下单还要区分是主扫还是被扫,因为这几种区分是实际和线下场景相匹配的,为了降低耦合,这时我们会分别提供一个接口来处理各种请求。
既然是每种场景对应一个接口,我们又可以想到,通常接口都是需要进行安全性校验的,如MD5、RSA及SHA1各种加密,保证交易的安全性。为了和核心业务区分开来,我们通常会在前置业务中甚至在filter端就把这些校验就完成掉,避免如果校验出错进到业务层去。既然每个接口都有诸如前置校验----核心处理----后置处理的逻辑,那我们可以把上面的设想再细分一下,即:
// 前置业务---参数校验,加密等操作 beforeService() // 核心业务处理----预下单、支付等核心业务 doService() // 后置业务处理----保存订单、保存流水、发送通知等后置业务 afterService() |
那么代码层应该如何去设计呢?可否这样,像下面的UML图:
下面对各个组件进行讲解:
SDKService
最顶层的抽象,一般业务类不需要实现它,需要需要扩展该接口,直接继承即可。
SDKNoCardService
无卡交易相关能力,扩展SDKService
SDKRomoteService
调用远程方法交易相关能力,扩展SDKService,如远程调用微信下单接口
AbstractSDKService
抽象类,实现SDKService,同时定义上面的三个声明周期方法,相当于一个模板,具体实现由子类去重写即可
SDKNoCardServceImpl
无卡交易业务实现类,继承抽象类,因为需要获取卡号,所以实现SDKNoCardService接口
SDKCardTradeServiceImpl
刷卡交易业务实现类,继承抽象类,因为需要调用远程服务,所以实现SDKRomoteService接口
这样做的好处在于,如果我需要引入一个新的需求,比如说小额免签免密,那么我只需要创建一个新类继承抽象类AbstractSDKService即可,如果小额免签免密需要一个新的能力,那么只需要新增一个接口扩展SDKService即可。下面给出相应的测试代码:
SDKService.java
package com.example.interfaces; import java.util.Map; import javax.servlet.http.HttpServletRequest; public interface SDKService<T,R> { /** * 业务处理 * @param request * 请求 * * @return */ public R service(HttpServletRequest request); /** * 业务处理 * @param body * 客户端请求消息体对象 * * @param request * 请求 * * @return */ public R service(T body, HttpServletRequest request); /** * 业务处理 * @param body * 客户端请求消息体对象 * * @param headers * 客户端请求头信息对象 * * @return */ public R service(T body, Map<String, String> headers); } |
SDKRemoteService.java
package com.example.interfaces; public interface SDKRemoteService<T, R> extends SDKService<T, R>{ /** * 调用远程交易,比如说调用微信支付 * @return */ public R call(); } |
SDKNoCardService.java
package com.example.interfaces; public interface SDKNoCardService<T, R> extends SDKService<T, R> { /** * 获取卡号 * @return */ public R getCardNo(); } |
AbstractSDKService.java
package com.example.interfaces; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; /** * 传入参数T,返回结果R * * @author huangjiawei * * @param <T> * @param <R> */ public abstract class AbstractSDKService<T, R> implements SDKService<T, R> { // 前置处理,比如可以对参数进行校验,由子类选择性重写 protected void beforeService() { }; /** * 核心业务方法,由不同业务子类选择性去重写 * * @return */ protected abstract R doService(); // 后置处理,比如记录业务结果,由子类选择性重写 protected void afterService() { }; /** * 接受请求 */ @Override public R service(HttpServletRequest request) { // 模拟请求体 System.err.println("service with only request!"); return this.service(null, request); } /** * 处理请求体 */ @Override public R service(T body, HttpServletRequest request) { // 模拟请求体 Map<String, String> headers = new HashMap<>(); System.err.println("service with body and request!"); return this.service(body, headers); } /** * 处理请求头和实际业务逻辑 */ @Override public R service(T body, Map<String, String> headers) { System.err.println("service with bofy and headers!"); R response = null; // 记录日志 //...... // 前置处理 beforeService(); // 核心处理 doService(); // 后置处理 afterService(); // ...... return response; } } |
SDKCardTradeServiceImpl.java
package com.example.interfaces; public class SDKCardTradeServiceImpl<T,R> extends AbstractSDKService<T,R> implements SDKRemoteService<T,R> { // 在这里可以选择性重写父类的before()方法 @Override protected void beforeService() { super.beforeService(); System.err.println("SDKCardTradeServiceImpl before!"); } @Override protected R doService() { // 处理刷卡特定业务逻辑 System.err.println("SDKCardTradeServiceImpl doService!"); return null; } /** * 我同时拥有调用远程方法的能力,所以我实现了SDKRemoteService接口 */ @Override public R call() { return null; } // 在这里可以选择性重写父类的after()方法 @Override protected void afterService() { super.afterService(); System.err.println("SDKCardTradeServiceImpl afterService!"); } } |
SDKNoCardServceImpl.java
package com.example.interfaces; /** * 无卡交易核心业务处理 * @author huangjiawei * */ public class SDKNoCardServceImpl<T,R> extends AbstractSDKService<T,R> implements SDKNoCardService<T,R> { // 在这里可以选择性重写父类的before()方法 @Override protected R doService() { return null; } /** * 我同时拥有获取卡号的能力,所以我实现了SDKNoCardService接口 */ @Override public R getCardNo() { return null; } // 在这里可以选择性重写父类的after()方法 @Override protected void afterService() { super.afterService(); System.err.println("SDKNoCardServceImpl 后置处理"); } } |
Main.java
package com.example.interfaces; public class Mian { public static void main(String[] args) { SDKCardTradeServiceImpl<String,String> o = new SDKCardTradeServiceImpl<>(); o.service(null); System.err.println("======================================"); SDKNoCardServceImpl<String, String> p = new SDKNoCardServceImpl<>(); p.service(null); } } |
程序输出:
service with only request! service with body and request! service with bofy and headers! SDKCardTradeServiceImpl before! SDKCardTradeServiceImpl doService! SDKCardTradeServiceImpl afterService! ====================================== service with only request! service with body and request! service with bofy and headers! SDKNoCardServceImpl 后置处理 |
注意我们实际调用的时候只需要调用顶层的service()即可,面向接口编程。
二、总结
认真看,其实这个已经定义好了一个大模板了,会非常实用的。小编认为,一般的设计思路是先采用一个顶层接口进行高层抽象,然后创建其他接口扩展顶层接口,在顶层接口下建立一层抽象层,提供默认的实现,定义模板方法给不同的业务类重写。当然,处理本文的做法,一般处理web请求我们也可以通过AOP切面的方式对前置处理和后置处理进行拦截,即利用环绕通知即可,但是,采用aop的缺点可能会获取不到request请求的参数(没测过,大佬们说会,我也不清楚为什么,等你来告诉小编!),本文的方法利用了java面向对象的特性,扩展性会更好点,不过略抽象!
本文讲解的比较抽象,很多地方可能没有讲的特别清楚,同学们可以评论,我们一起探讨探讨。
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。