今天这篇文章所涉及的相关观点做法,按照我们公司某位架构师的说法就是,架构这东西没有对错之分,集思广益,不一定我的做法就是对的,不一定我的做法就是最优的,小编和大家一起探讨探讨。
一、场景
首先我们模拟一个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面向对象的特性,扩展性会更好点,不过略抽象! 本文讲解的比较抽象,很多地方可能没有讲的特别清楚,同学们可以评论,我们一起探讨探讨。