应用场景
一个代理服务对接多个产品服务,需要提供一个公共接口业务来对接各个目标平台的业务,并保存一份处理记录数据于自己服务的数据库中。
架构图
责任链模式提出的动机
请求的分发引出了架构的问题。
而对于这种条件判断,处理具体请求的场景,我们首先想到的解决方案一般是选择结构(使用条件语句进行业务筛选)。
选择结构用于判断给定的条件,根据判断的结果判断某些条件,根据判断的结果来控制程序的流程。
代码示例:
从上述例子中我们可以看出,使用条件语句的选择结构,虽然业务结构清晰简单,但各个条件分支业务处理的方法都集中在同一个类中,违反了“单一职责”的原则,耦合性很高,测试和维护难度大。
责任链模式
责任链模式是一种灵活的、可插拔式的解决方案,可以最大程度解决选择结构引发的问题,提高系统的灵活性、可拓展性,降低系统的耦合性。
责任链模式简单说就是将能够处理同一类请求的对象连成一条链,让请求沿着链走,链式传递,当该节点的对象能够处理请求的时候就由它来处理,不能处理就往下传递,请求发送者无须知道请求在何时、何处以及如何被处理,实现了请求发送者与处理者的解耦。生活中的打扑克游戏就是典型的责任链模式。
优点
1)降低耦合度。它将请求的发送者和接收者解耦。
2)简化了对象。使得对象不需要知道链的结构。
3)提高给对象指派职责的灵活性。可以灵活地改变链内的成员或者调动它们的次序,允许动态地新增或者删除。
4)提高了可拓展性。增加新的请求处理类很方便。
模板方法模式提出的动机
针对当前业务场景,请求对象是一种统一的数据结构,只是部分数据内容有所区别,它无法被目标平台直接处理响应。这就需要我们对请求数据进行进一步的封装处理。
对请求数据的封装处理引出了代码复用性的问题。
因为虽然处理后的结果数据不一样,但是宏观处理过程上却有一定的相似性。在本业务场景中,例如:请求参数的校验、数据库预存储、流程控制、构建请求参数、请求转发、目标平台处理完请求后对代理服务数据库数据进行后续处理等等。
请求的处理过程几乎会涉及到上述的大部分步骤,在各个步骤的业务处理上,有的步骤的业务处理是一样的,有的则是要做针对性处理的。
因为涉及重复的业务,我们会考虑到将可复用的代码单独拎出来,当流程走到可复用代码处理的业务步骤时,进行相关的调用。
一般情况我们会将这部分代码单独放到一个拓展类中,谁需要的时候,谁就调用,这样来避免代码的重复编写。这样的处理虽然简单,但是却有一些缺点:
1)每次我们调用的都需要创建一个这个类对象,会造成系统资源的浪费。
2)同时,我们对拓展公共方法的调用都需要进行代码级别的调用,当后期来了新需求需要拓展一套处理业务的时候都需要重复添加调用的代码,增加了代码量。
3)另外对于整体的处理过程,拓展类是存在过程方法缺失的,当我们想了解业务的整体处理流程的时候,是不太方便的。
当系统中算法的骨架固定,而算法的实现可能有很多种的时候,就可以使用模板方法模式。而针对当前的业务场景,算法的骨架就是请求数据的所有处理步骤,而算法的实现就是每一个步骤的处理逻辑。因此采用模板方法模式就很合适了。
模板方法模式
模板方法模式即父类定义一个操作中的算法框架,将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤的具体实现。这是通过继承的方式来实现对象/代码的复用,遵守了开闭原则。
优点
1)封装不可变的部分,拓展可变的部分,遵守开闭原则。
2)提取公共代码进行复用,便于维护。
3)子类实现算法的某些具体细节,有助于算法的拓展。
步骤
针对此种业务场景,采用责任链模式+模板方法模式来处理,能够降低系统的耦合度,提高可拓展性,同时实现代码的高效复用。
构造责任链模式架构
构造抽象父类,定义节点的引用,声明链传递方法
构造子类,实现链传递方法
AProduct
BProduct
CProduct
添加业务控制方法,构造请求处理节点并连成链调用请求
在责任链模式架构基础上搭建模板方法模式架构
改造抽象父类-定义步骤
编写抽象父类中非抽象方法的业务,即各子类通用的方法
在子类中实现父类定义的抽象方法,进行具体业务的处理
以A产品子类实现的process()方法为例。
@Override
public Result process(Request request) {
//校验公共参数
Result checkRequestFieldResult = checkRequestField(request);
if(null!=checkRequestFieldResult){
return checkRequestFieldResult;
}
//在请求平台之前先往代理服务数据库中添加一条记录
Result insertResult = insertBeforeRequestPlatform(request);
if(null!=checkRequestFieldResult){
return insertResult;
}
//构造请求A平台的请求头数据
HttpHeaders header = createHeader(request);
//构造请求A平台的请求参数
JSONObject params = createParams(request);
HttpEntity<Object> requestParam = new HttpEntity<>(params, header);
try{
//请求A平台的业务处理
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.postForEntity("A-URL", requestParam, String.class);
String bodyStr = response.getBody();
JSONObject body = JSONObject.parseObject(bodyStr);
if (0!=body.getInteger("errorCode")){
setErrorReason(request.getId(), body.getString("errorMessage"));
}
updateAfterRequestPlatform(request.getId());
} catch (Exception e) {
setErrorReason(request.getId(), "A平台交互异常");
return ResultUtil.error(500, "与A平台交互异常");
}
return ResultUtil.success();
}