在业务实现中,遇到一种场景,从开始传入多方的数据,经过多个不同种类环节的分析,最终得到一个分析结果。在对业务逻辑抽象后,我们实现了一种基于树形的分析结构,达到了比较理想的效果,优点主要有以下几个:
- 分析逻辑复用,避免了重复的代码;
- 结构简洁,清晰,维护成本低;
- 分析结构和结果的可视化,简化了分析流程;
考虑到这种分析方式可以在其他类似的场景下复用,因而做个简单分享记录。后面会从以下几个方面介绍,首先是分析结构的模块设计以及核心点的功能,之后简单介绍下分析流程。
一、分析结构模块和功能
1、处理上下文
实现为一个Context,主要包含入口数据,一个用作节点跟踪的有序List。入口数据是包装的业务参数,本例为待分析的原始数据;节点跟踪用于生成处理的结果链。代码模板如下,可以根据自己不同的需求做扩展。
public class ProcessContext {
private LinkedList<ProcessNode> traces = Lists.newLinkedList();
private CompareRequest data;
...
/**
* 获取最终结果,待修改
*
* @return
*/
public ProcessNode finalResult() {
if (CollectionUtils.isNotEmpty(traces)) {
return traces.getLast();
}
return null;
}
//结果链
public String printShowChain() {
if (CollectionUtils.isEmpty(showtraces)) {
return "empty";
}
StringBuilder chain = new StringBuilder();
for (String node : showtraces) {
chain.append(node).append(" -> ");
}
chain.append("end");
return chain.toString();
}
}
2、树形处理结构和 节点
最初的方案是定义为一个ProcessTree,包含名称,描述,版本,以及一个根节点,树形结构采用XML文件形式定义,递归构造。之后采用的是直接以节点直接作为根开始,分类处理,将节点注册为SpringBean,也是配置在XML文件里。其中,节点包括描述,左右子节点,以及处理逻辑的接口。在树形中,叶子节点便是最终的原因,记录的整个处理链可以作为分析记录和辅助的分析数据。如下介绍:
/**
* 类似二叉树流程节点
*/
public class ProcessNode {
private String reason;
private String desc;
private ProcessNode left;
private ProcessNode right;
private ValidCheckService validCheckService;
}
//获取下一个节点,返回null则终止
public ProcessNode next(NodeType result) {
return (left != null && result == NodeType.GO_LEFT) ? left : ((right != null && result == NodeType.GO_RIGHT) ? right : null);
}
//左右节点标记
public enum NodeType {
GO_LEFT(-1, "left"),
GO_RIGHT(1, "right");
private int code;
private String desc;
NodeType(int code, String desc) {
this.code = code;
this.desc = desc;
}
public int getCode() {
return this.code;
}
}
}
初版树形定义和对应方法
/**
* 逻辑处理树形,从root遍历
*/
public class ProcessTree {
private String name;
private String id;
private String version;
private ProcessNode root;
public ProcessTree() {
}
/**
* 全部执行,true --> goright false --> goleft
*
* @return
*/
public void processAll(ProcessContext context) throws ProcessException {
List<ProcessNode> resultTrace = context.getTraces();
ProcessNode node = root;
ProcessNode.NodeType nodeType;
while (node != null) {
try {
ValidCheckService validCheckService = node.getValidCheckService();
//叶子节点,包含结论
if (null == validCheckService) {
break;
}
nodeType = validCheckService.dataCheck(context.getData()) ? ProcessNode.NodeType.GO_RIGHT : ProcessNode.NodeType.GO_LEFT;
node = node.next(nodeType);
} catch (Exception e) {
throw new ProcessException(e);
} finally {
resultTrace.add(node);
}
}
}
/**
* 转换为echart 树形图
*
* @return
*/
public TreeNode convertToEchartTree() {
ProcessNode node = root;
TreeNode treeNode = new TreeNode(root.getDesc());
initTreeNode(treeNode, node);
return treeNode;
}
/**
* 递归遍历左右子树,作为转换
*
* @param treeNode
* @param processNode
* @see ProcessTree#convertToEchartTree()
*/
private void initTreeNode(TreeNode treeNode, ProcessNode processNode) {
if (processNode == null) {
return;
}
if (processNode.getLeft() != null) {
TreeNode left = new TreeNode(processNode.getLeft().getDesc());
treeNode.addChildren(left);
initTreeNode(left, processNode.getLeft());
}
if (processNode.getRight() != null) {
TreeNode right = new TreeNode(processNode.getRight().getDesc());
treeNode.addChildren(right);
initTreeNode(right, processNode.getRight());
}
}
}
echart的Tree Node
/**
* Echart tree base node
* http://echarts.baidu.com/option.html#series-tree.type
*/
public class TreeNode {
private String name;
private List<TreeNode> children = Lists.newLinkedList();
public TreeNode(String name) {
this.name = name;
}
3、处理逻辑抽象接口
接口只有一个方法,根据不同的业务逻辑实现,最终调用各自对应的处理逻辑。
public interface ValidCheckService {
public boolean dataCheck(CompareRequest compareRequest) throws ProcessException;
}
二、分析流程和结果展现
实现的分析流程展现,最终通过echart画出图形,实际分析即从根节点开始,按照各自的处理逻辑,由结果确定下一个节点,直到遍历到叶子节点为止:
最终的分析链类似如下结构,业务原因不提供实际数据,见谅:
A->B->C->D->E->F->end
以上,就是分析流程的简单介绍,业务中也得到了良好的效果,有考虑过两个想法,未有实现。一是在创建树形时,可以用类似Activity等流程引擎的工具,做到创建可视化,但这个工作量可能较大,而且目前的业务中,分析流程基本固定,变化很少,单纯操作XML文件足够,暂不考虑这种方案;其次,分析链可以和展示树形结合,带一个固定key,然后按照链的数据在树形上突出展现,当时在echart一时没有找到简洁的接口,也就作罢。
感谢阅读。
网友评论