



















由于业务需要,近期看了一下平台应用如何调用「招商银行(CMB)」和「平安银行(PAB)」接口进行业务交互。于是在思考,针对于这样的业务场景,是否需要考虑 设计模式、是否需要考虑 后期维护 、是否考虑 易扩展性 等。
接下来,我将从对接银行接口出发,列举出三套实现方案。从 基础实现 -> 接口 -> 高级抽象 维度来实现。
所谓的「基础实现」就是过程式编码,满足业务功能,不考虑任何扩展、设计模式等因素;主打一个能用就行。
CmbService:招商银行的接口逻辑
// 招商银行接口
public class CmbService {
// 获取子账号余额
public CmbSubAccountBalance getSubAccountBalance(CmbSubAccountBalanceParam param) {
// 请求招商银行
String s = this.request(param);
// 响应消息序列化成对象
return JSONUtil.toBean(s, CmbSubAccountBalance.class);
}
public String request(Object param) {
//...请求银行,涉及到的操作
// 1. 加签
// 2. 加密
// 3. 解密
}
}
PabService:平安银行的接口逻辑
// 平安银行接口
public class PabService {
// 获取子账号余额
public PabSubAccountBalance getSubAccountBalance(PabSubAccountBalanceParam param) {
// 请求平安银行
String s = this.request(param);
// 响应消息序列化成对象
return JSONUtil.toBean(s, PabSubAccountBalance.class);
}
public String request(Object param) {
//...请求银行,涉及到的操作
// 1. 加签
// 2. 加密
// 3. 解密
}
}
BizService:业务层调用
public class BizService {
// 获取子账号余额
public String getSubAccountBalance(AccountParam param) {
// 判断银行类型
if("CMB".equals(param.getBankType())) {
// 请求参数转换成招行接口的入参
CmbSubAccountBalanceParam cmbParam = new CmbSubAccountBalanceParam(param);
// 请求招商银行获取子账户余额
CmbSubAccountBalance accountBalance = cmbSubAccountBalance.getSubAccountBalance(cmbParam);
return accountBalance.getBalance();
}
if("PAB".equals(param.getBankType())) {
// 请求参数转换成平安接口的入参
PabSubAccountBalanceParam pabParam = new PabSubAccountBalanceParam(param);
// 请求平安银行获取子账户余额
PabSubAccountBalance accountBalance = pabSubAccountBalance.getSubAccountBalance(pabParam);
return accountBalance.getBalance();
}
}
}
上述代码,快速的实现了业务功能。很直观的把业务需求,转换成最直观的代码。
优点:
缺点:
所谓的「接口」就是把公共的请求,抽象成一个接口。统一管理,不同的银行通道实现接口。再通过设计模式(策略模式、工厂模式)获取具体的实现。业务层调用工厂类获取具体接口实现即可。
BankTemplate:银行统一接口
// 银行统一接口
public interface BankTemplate {
// 获取子账号余额
SubAccountBalance getSubAccountBalance(SubAccountBalanceParam param);
}
CmbBankTemplate:招商银行接口实现
// 招商银行接口实现
public class CmbBankTemplate implements BankTemplate {
// 获取子账号余额
public SubAccountBalance getSubAccountBalance(SubAccountBalanceParam param) {
// 入参转换成招行请求参数字段
Map<String, String> paramMap = this.buildMap(param);
// 请求银行
String response = this.request(paramMap);
// 把 response 响应转换成统一的 SubAccountBalance 对象
return this.buildSubAccountBalance(response);
}
public String request(Object param) {
//...请求银行,涉及到的操作
// 1. 加签
// 2. 加密
// 3. 解密
}
}
PabBankTemplate:平安银行接口实现
// 平安银行接口实现
public class PabBankTemplate implements BankTemplate {
// 获取子账号余额
public SubAccountBalance getSubAccountBalance(SubAccountBalanceParam param) {
// 入参转换成平安请求参数字段
Map<String, String> paramMap = this.buildMap(param);
// 请求银行
String response = this.request(paramMap);
// 把 response 响应转换成统一的 SubAccountBalance 对象
return this.buildSubAccountBalance(response);
}
public String request(Object param) {
//...请求银行,涉及到的操作
// 1. 加签
// 2. 加密
// 3. 解密
}
}
BankTemplateFactory:BankTemplate工厂类
public class BankTemplateFactory {
public static BankTemplate getTemplate(String type) {
// 招商银行
if ("CMB".equals(type)) {
return new CmbBankTemplate();
}
// 平安银行
if ("PAB".equals(type)) {
return new PabBankTemplate();
}
}
}
BizService:业务层调用
public class BizService {
// 获取子账号余额
public String getSubAccountBalance(SubAccountBalanceParam param) {
// 根据银行类型获取具体BankTemplate实现
BankTemplate bankTemplate = BankTemplateFactory.getTemplate(param.getBankType());
// 请求银行获取子账户余额
SubAccountBalance subAccountBalance = bankTemplate.getSubAccountBalance(param);
return subAccountBalance.getBalance();
}
}
上述代码,统一了接口,方便了扩展和后期维护;
优点:
BankTemplateFactory 工厂类,获取具体实现BankTemplate 接口,以及在 BankTemplateFactory 工厂类中加判断(可以优化成不需要手动加判断,不在此次谈论范围内)第二套「接口」方案,已经解决了第一套「基础实现」方案中的不足的方面,满足了业务系统后续扩展和后续维护等实际需求。
所谓的「高级抽象」就是分析业务后,把业务模型抽象化。那么对接银行的业务如何抽象化呢?
通过分析发现,业务需求是请求银行接口获取相应的数据,那么抽象出来就是需要一个请求银行的接口( BankInterface )。
不同银行请求入参不同、加解密方式不同,所以需要有一个适配器去转换( BankInterfaceAdapter )。
两个银行都提供了「查询子账号余额」的接口,但请求的 URL 是不同的,为了简化URL设置,可以自定义注解( @BankApi(path = “/V1.0/bedl/SubAccountBalanceQuery”, method = HttpMethod.POST) )。
大致代码逻辑如下:
BankInterface:银行接口
/**
* 银行接口
*/
public interface BankInterface {
/**
* 银行接口适配器
* @return
*/
BankInterfaceAdapter adapter();
/**
* 子账号余额查询
* @param param
* @return
*/
JSON subAccountBalanceQuery(BankApiParam param);
}
BankInterfacePab:平安银行接口
/**
* 平安银行接口
*/
@Type(value = BankType.PAB)
public interface BankInterfacePab extends BankInterface {
@BankApi(path = "/V1.0/bedl/SubAccountBalanceQuery", method = HttpMethod.POST)
@Override
JSON subAccountBalanceQuery(BankApiParam param);
}
BankInterfaceAdapter:银行适配器
/**
* 银行适配器
*/
public interface BankInterfaceAdapter {
/**
* 构建请求参数
* @param obj
* @return
*/
BankApiParam buildParam(Object obj);
/**
* 前置处理
* @param param
*/
void preProcessor(BankApiParam param);
/**
* 后置处理
* @return
*/
String postProcessor(String msg);
/**
* 发送请求
* @param param
* @return
*/
String process(BankApiParam param);
/**
* 子账号余额适配
* @param json
* @return
*/
SubAccountBalance subAccountBalanceQuery(JSON json);
}
BankInterfaceAdapterPab:平安银行适配
/**
* 平安银行适配
*/
public class BankInterfaceAdapterPab implements BankInterfaceAdapter {
public final static BankInterfaceAdapterPab INSTANCE = new BankInterfaceAdapterPab();
@Override
public BankApiParam buildParam(Object obj) {
return null;
}
@Override
public void preProcessor(BankApiParam param) {
}
@Override
public String postProcessor(String msg) {
return null;
}
@Override
public String process(BankApiParam param) {
return null;
}
@Override
public SubAccountBalance subAccountBalanceQuery(JSON json) {
return null;
}
}
BankProxy:银行代理类
/**
* 银行代理类
*/
public interface BankProxy {
/**
* 获取子账号流水
* @param param
* @return
*/
SubAccountBalance getSubAccountBalance(SubAccountBalanceParam param);
}
BankProxyImpl:银行代理类实现
/**
* 银行代理类实现
*/
public class BankProxyImpl implements BankProxy {
private final BankInterface bankInterface;
private final BankInterfaceAdapter bankInterfaceAdapter;
public BankProxyImpl(BankInterface bankInterface) {
this.bankInterface = bankInterface;
this.bankInterfaceAdapter = bankInterface.adapter();
}
@Override
public SubAccountBalance getSubAccountBalance(SubAccountBalanceParam param) {
BankApiParam bankApiParam = bankInterfaceAdapter.buildParam(param);
JSON json = bankInterface.subAccountBalanceQuery(bankApiParam);
return bankInterfaceAdapter.subAccountBalanceQuery(json);
}
}
RpcClientProxy:请求银行RPC,反射
/**
* 请求银行RPC,反射
*/
public class RpcClientProxy implements InvocationHandler {
@Getter
private final RpcClientProxyFactoryBean factory;
public RpcClientProxy(RpcClientProxyFactoryBean factoryBean) {
this.factory = factoryBean;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
RpcClientProxyFactoryBean:RPC请求代理工厂
/**
* RPC请求代理工厂
*/
public class RpcClientProxyFactoryBean implements FactoryBean<BankInterface> {
private final Class<? extends BankInterface> serviceInterface;
public RpcClientProxyFactoryBean(Class<? extends BankInterface> clz) {
this.serviceInterface = clz;
}
@Override
public BankInterface getObject() throws Exception {
return (BankInterface) Proxy.newProxyInstance(this.serviceInterface.getClassLoader(),
new Class[]{this.serviceInterface},
new RpcClientProxy(this));
}
@Override
public Class<?> getObjectType() {
return this.serviceInterface;
}
@Override
public boolean isSingleton() {
return true;
}
}
BankInterfaceManager:统一银行接口管理
/**
* 统一银行接口管理
*/
public class BankInterfaceManager {
/**
* 获取具体的银行代理类
* @param bankType
* @return
*/
public BankProxy getBankProxy(BankType bankType) {
BankInterface bankInterface = null;
return new BankProxyImpl(bankInterface);
}
}
这段时间,每晚入睡了,大脑中总在思考当前的业务应该如何抽象,如何设计。自己已经魔怔了!
其实我们写代码,本质是让现实世界的需求,在计算机世界中被理解和执行。如果只是这样,我可能不会要求自己的代码写的像 诗 一样!
编码的最终形态,表达了对业务理解的抽象能力,也表达了对使用语音特性的理解(比如面向对象、面向接口、面向抽象)。有时候固执的要求自己写出所谓的 完美 代码,那只会让自己陷入痛苦之中。
在时间、成本、维护性等方面寻找平衡,放弃过渡思考、过渡设计。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。