还是基于《消失的字段》和《消失的消息》两篇文章中提到的供应链金融项目。该项目支持多种融资模式,比如:应收账款(YSZK)融资、活体质押(HTZY)融资、预付信用(YFXY)融资等。不同的融资模式业务流程不尽相同,包含不同的请求交易;即使相同的请求交易,交易请求报文也可能不一样。
针对这种情况,如果设计不合理,将导致类的数量暴增。假如5个场景,每个场景3个交易请求,各不相同,就要处理15种请求,如果每种请求都由一个服务实现类处理,则需要15个实现类(只有这样子才能保证遵循开闭原则)。如果扩展一个场景,则需要增加3个类;如果增加一个交易请求,则需要增加5个类。
根据以上的描述,采用桥接模式来实现该需求的设计开发,既能控制类数量暴增的情况,同时也能满足开闭原则。
先上一张UML类图,如下:
上图设计了3个接口:
接下来,贴出应收账款融资场景的相关类代码:
@Service
public interface BankBaseService<T extends BaseBodyReq> {
Resp sendToBank(PubHeadReq head, T body, String serviceUrl);
}
@Service
public class BankBaseServiceImpl<T extends BaseBodyReq> implements BankBaseService<T> {
@Override
Resp sendToBank(PubHeadReq head, T body, String serviceUrl) {
// 向银行发送请求报文逻辑
...
}
}
@Service
public interface BankParamBuilder {
// 构建银行接口-企业准入申请的参数
BankParam buildEnterpriseInfoApply(BizHeadReq head, JSONObject body) throws Exception;
// 构建银行接口-授信申请的参数
BankParam buildCreditApply(BizHeadReq head, JSONObject body) throws Exception;
// 构建银行接口-融资申请的参数
BankParam buildFinanceApply(BizHeadReq head, JSONObject body) throws Exception;
}
// 应收账款融资场景银行接口请求报文构建实现类
@Service("yszkBankParamBuilder")
public class TradeDebtBankParamBuilder implements BankParamBuilder {
// 构建不同交易的请求报文
...
}
@Service
public interface BankService {
// 企业准入申请
BaseBodyResp enterpriseInfoApply(BizHeadReq head, JSONObject body) throws Exception;
// 授信申请
BaseBodyResp creditApply(BizHeadReq head, JSONObject body) throws Exception;
// 融资申请
BaseBodyResp financeApply(BizHeadReq head, JSONObject body) throws Exception;
}
// 应收账款融资场景银行接口服务实现类
@Service
public class YSZKBankServiceImpl extends BankBaseServiceImpl implements BankService {
@Autowired
private BankParamBuilder yszkBankParamBuilder;
@Override
public BankBaseBodyResp enterpriseInfoApply(BizHeadReq head, JSONObject body) throws Exception {
// 构建银行接口请求参数
BankParam<EnterpriseInfoApplyReq> bankParam = yszkBankParamBuilder.buildEnterpriseInfoApply(head, body);
// 发送参数到银行接口服务
return sendToBank(bankParam.getHead(), bankParam.getBody(), bankParam.getServiceUrl());
}
@Override
public BankBaseBodyResp creditApply(BizHeadReq head, JSONObject body) throws Exception {
throw new Exception("应收账款融资场景不支持接口申请授信!");
}
@Override
public BankBaseBodyResp financeApply(BizHeadReq head, JSONObject body) throws Exception {
// 构建银行接口请求参数
BankParam<FinanceApplyReq> bankParam = yszkBankParamBuilder.buildFinanceApply(head, body);
// 构建银行接口请求参数
return sendToBank(bankParam.getHead(), bankParam.getBody(), bankParam.getServiceUrl());
}
}
桥接模式很好地解决了多维度关联扩展问题。如果新增一个融资场景,只需要增加两个类:BankService接口的实现类和BankParamBuilder接口的实现类,与交易请求方法数量无关,而且本次扩展不会影响原来场景的实现代码。
可能读者会发现一个问题,如果BankService接口增加一个交易方法呢,其当前各个场景的实现类代码都需要调整。这个问题,可以从业务角度考虑,提前考虑整个融资流程中涉及的所有需要与银行交互的交易,这个流程在各个银行应该都有一套固定业务流程,是可以提前考虑全面的。
另外,即使新增了一个交易,也并不需要全部场景都要实现,允许某些融资场景不支持一些银行交易。比如:YSZKBankServiceImpl代码中就不支持通过接口实现授信申请,必须去柜台办理。