大量研究结果表明人类通过图形获取信息的速度比通过阅读文字获取信息的速度要快很多,人脑对视觉信息的处理要比书面信息容易得多。而逻辑思维是一种比较抽象的思维,如果能把逻辑可视化将会大大提高接收效率。
可视化编程是指编程过程中可随时看到结果,程序与结果的调整同步;
可视化编程泛指一切使用可视化元素的操作,代替文本(编码)输入的程序设计方式,它大体上就像画流程图一样,通过连接若干“盒子”和“箭头”来实现程序逻辑;
经典的开发模式是,产品提供流程图,与研发进行沟通,研发再根据逻辑进行coding,经典开发模式的痛点在于:
1. 逻辑复杂、无全局视角;
2. 逻辑代码需要Coding:现有的页面开发出的页面与逻辑之间高度耦合,针对不同的页面,往往需要根据页面需求进行单独的代码开发;
3. 团队沟通耗费成本:产品,研发,测试对不同的逻辑所产生的结果需要重复沟通,确认;
经典的开发模式是产品提供流程图,研发根据流程图进行逻辑开发,逻辑复杂时,研发需要和产品多次确认梳理逻辑关系;
可视化开发模式,产品只需编辑逻辑节点图,会自动生成AST抽象树,然后对AST抽像树进行自动解析,生成逻辑代码。只需要产品确认逻辑即可;
逻辑清晰,可根据传入字段的不同,自动规划逻辑执行路线与结果;
代码自动生成,无需研发人员coding;
逻辑严格按照产品逻辑,不会导致多人沟通造成逻辑遗漏或误解;
以下展示的是,根据字段的不同(用户身份状态,用户实名,信用等级等信息),显示的逻辑走向以及最终对业务楼层"京腾联名卡"展示的影响:
左边区域是业务预览区域,根据配置确定楼层的三种展示情况:显示/隐藏/置灰;
中间区域是逻辑预览区域,根据配置字段的不同,展示不同的逻辑走向(其中,红线标识为配置字段后的逻辑走向);
右边区域是配置区域,展示的为生成的AST抽象语法树;
可以看到,在逻辑预览区,根据接口字段配置的不同,可自动生成一条逻辑走向图,即红线标注部分。
逻辑关系图是怎么构建的呢?
逻辑关系图是由 逻辑元素+类别+关系 构成的;
逻辑元素是指可视化的图形结构,比如矩形、圆形、菱形、方形等;
类别是指用于限定逻辑元素在逻辑关系图中位置的使用条件或者使用方式。比如,该类别可包括:逻辑的起始类别、逻辑判断类别以及状态类别;
逻辑关系是指两个逻辑元素之间的联系;
下图展示的逻辑关系图为例,A(矩形)、B(菱形)、C(圆形)以及D(方形)的图形均为逻辑元素,其中,A的类别为起始类别、B的类别为判断逻辑类别、C以及D的类别均为状态类别;A到B为一个连接关系,B到C为一个连接关系,等等,后续我们的可视化都是基于这个规则实现的。
有了上述规则,我们可以构造一个简单的逻辑关系图,构建出逻辑关系图:
要生成如上图所示的逻辑图,需要以下步骤:
添加目标逻辑元素。当目标逻辑元素存在相邻的逻辑元素时,在目标逻辑元素与所述相邻的逻辑元素之间,生成连接关系标识;
由于不满足添加条件的逻辑元素则不可被添加,因此,通过上述构建出逻辑关系图的过程,能够有效地提高逻辑关系图的准确率;
确定出逻辑配置信息对应的执行路线;将逻辑配置信息以及逻辑判断条件对应的执行路线添加到属性中。该过程主要包括以下几个部分:
* 节点操作:
节点操作包含(新增"&&"节点,新增"||"节点,配置表达式,删除节点)等部分,我们以新增节点为例;
* AST抽象树生成:
节点操作完成会自动生成AST抽象树;
* AST树解析生成代码片段:
同样,AST树解析成代码片段也是自动完成解析的,解析完成的代码片段见上图的预览表达式;
逻辑可视化主要分为以下三部分:
1.节点操作
节点操作包含(新增"&&"节点,新增"||"节点,配置表达式,删除节点)等部分,我们以新增节点为例。
/**
* @param treeData:现有节点树
* @param label:新增的节点类型(||,&&,'')
* @param node:当前操作的节点
*/
handleAdd = (treeData, label, currentNode) => {
// 获取当前节点的父节点
const parentNode = currentNode.parentNode;
// 如果添加的操作,与当前节点,或者当前节点的父节点一样,直接push一个新的节点,并改变其parentId
if (label === currentNode.type || label === parentNode.type) {
// 如果类型还是当前节点类型,则直接push新节点,父节点为当前节点
// 如果类型是当前父节点的类型,直接push新节点,父节点为当前节点的父节点
let parentId = label === parentNode.type ? parentNode.id : currentNode.id;
treeData.push(newNode);
newNode.parentId = parentId;
} else {
// 如果类型与当前节点(B节点)以及当前节点父节点(A节点)都不一致,则
// 1.新增一个节点(C)作为父节点,C的父节点指向A
treeData.push(newNodeC);
newNodeC.parentId = parentNode.id;
// 2.新增一个节点(D),该节点的父节点指向C
treeData.push(newNodeD)
newNodeD.parentId = newNodeC.id;
// 3.节点B修改父节点指向C
currentNode.parentId = newNodeC.id;
}
};
新增节点后,如果要进行逻辑表达式的配置,我们也提供了配置方式,如以配置 userStatus == '3010'为例,下图中各个字段的含义如下:
左-获取数据方式:分为动态与静态两种方式,动态方式为从接口获取,静态为直接写死;
左-接口:也就是本例中要获取"userStatus"字段所在的接口;
左-字段:级联方式选择该字段;
条件表达式:可选的有"大于,小于,等于"三种条件;
右边获取的方式同上;
通过对节点的操作,可以生成节点信息,节点信息包含,父节点id,当前节点类型,当前节点表达式,当前节点层级,在x,y方向的偏移等信息,通过这些信息可以画出可视化的逻辑树图。
2. AST抽象树生成
每个节点的信息都获取之后,根据这些节点信息进行递归,转换成一颗完整的抽象树,后续通过生成的抽象树,转换为代码片段。
/**
* 统计树中各个节点下子节点的数目
* @param treeData
* @param floor
*/
static transformTree(treeData, floor = 1) {
let rootCount = 0; // 记录每个子树下的节点数目
// 1. 循环该树
for (let i = 0; i < treeData.length; i++) {
const node = treeData;
node.floor = floor;
// 2. 该节点是否为 叶子 节点,如果是,当前节点的childNodeCount设置为1;
if (node.children.length === 0) {
node.childNodeCount = 1;
node.floorNumber = rootCount;
rootCount++;
} else {
// 如果不是叶子节点,则统计当前节点下所有叶子节点的 childNodeCount
const leafCount = LogicConfig.transformTree(node.children, floor + 1);
node.childNodeCount = leafCount;
node.floorNumber = rootCount;
rootCount += leafCount;
}
}
return rootCount;
}
3. 数据转换生成表达式
通过对刚刚生成的抽像树进行解析,可以生成代码片段。至此,在无研发参与的情况下,已经自动化的根据产品的流程图生成了代码片段。
/**
* @param data:当前节点
* @param parentType:父节点的类型(||,&&,'')
*/
getResult = (data, parentType) => {
// 判断节点没有子节点
const arr = [];
if (data.children.length === 0) {
// 直接返回当前节点的表达式
return data.tips;
}
// 如果存在子节点,则
for (let i = 0; i < data.children.length; i++) {
const child = data.children;
// 如果节点没有"||,&&"逻辑连接标识,直接添加到数组
if (child.type === LOGIC_TYPE.none) {
arr.push(child.tips);
}
// 如果该节点类型为"||,&&",则认为存在子表达式, 将其子节点添加到数组。
if (child.type === "||" || child.type === "&&") {
arr.push(this.getResult(child, data.type));
}
}
// 如果节点类型与父节点判断类型一致,认为是同级判断,则直接拼接即可
if (data.type === parentType) {
return arr.join(` ${[data.type]} `);
} else {
// 如果节点类型与父节点判断类型不一致,则认为是新的一级,需要括号拼接
return `(${arr.join(` ${[data.type]} `)})`;
}
};
通过以上三个步骤,即可实现,新增逻辑节点图,便可自动生成AST抽象树,并解析为逻辑代码,也就是图中所示的预览表达式。
1. 逻辑可视化在链路监控中的应用
目前我们的链路监控也使用了可视化思想,通过对调用方法的来源进行标识,生成调用链路的可视化走向图。
2. 逻辑可视化与猎户座的结合
猎户座是一个配置化引擎,通过在配置化引擎上层封装可视化用户界面,让页面搭建者直接在猎户座系统中通过可视化界面,实现0开发基础快速搭建页面。
但是这些界面,组件大多时候是依赖逻辑,有不同的展示的,而逻辑可视化的出现可以有效解决这一问题,通过可视化的逻辑配置,动态判断某些楼层,模块的展示逻辑,从而可以有效的搭建C端页面。
我们的工作也可以从复杂的逻辑开发抽离出来,只需要对组件库进行完善及开发即可。