背景:我之前写过一篇文章:《Liteflow逻辑编排可视化设计》,分享了我们对LiteFlow这个规则引擎的逻辑可视化设计,而我们这个可视化项目的目标不仅仅只是实现LiteFlow的逻辑可视化,而是实现LiteFlow的逻辑可视化编排。
接下来,我将通过系列文章的形式,进行LiteFlow逻辑可视化编排设计与实现的开发经验分享。
《LiteFlow逻辑可视化编排设计与实现》会以系列文章的形式,有步骤、有重点地分享我们对LiteFlow逻辑可视化编排的实现,目前该系列文章有如下5篇:
- LiteFlow逻辑可视化编排设计与实现 01-先导篇
- LiteFlow逻辑可视化编排设计与实现 02-数据模型篇(Model)
- LiteFlow逻辑可视化编排设计与实现 03-视图呈现篇(View)
- LiteFlow逻辑可视化编排设计与实现 04-操作逻辑篇(Control)
- LiteFlow逻辑可视化编排设计与实现 05-后记篇(前端代码已开源,文末有仓库地址)
以下是《LiteFlow逻辑可视化编排设计与实现 02-数据模型篇(Model)》的文章正文。
02-数据模型篇(Model)
回顾一下我们在《先导篇》中提到过的内容,作为一名前端开发,我们需要特别关注的要素有三个——数据(Model)、视图(View)和逻辑(Control),即“MVC”——我接下来也是使用“MVC三要素”的知识框架来进行LiteFlow逻辑可视化编排系统的拆解、组合、设计和实现的:
而“数据”,或者“模型”,或者“数据模型”,或者“概念模型”,是我们开发之前需要最先分析和设计的部分,而具体到对LiteFlow进行逻辑可视化编排的模型设计上,我们的目标是将EL表达式的操作符(Operator)进行建模,最终我们将EL表达式建模成了由ELNode组成的一棵树:
1、EL表达式
LiteFlow的逻辑编排,都是通过EL表达式来实现,比如一个EL表达式的例子:
<chain name="chain1">
THEN(
a,
WHEN(b, c, d),
e
);
</chain>
其中“THEN”和“WHEN”是EL表达式的关键字,分别表示串行编排和并行编排,而“a”“b”“c”“d”“e”则是5个逻辑组件,由此组成了一个串行和并行编排的组合——即先执行“a”组件,然后并行执行“b”“c”“d”组件,最后执行“e”组件。
LiteFlow对可执行的逻辑流程进行建模,主要包括以下2个部分:
-
1、逻辑组件(组件节点):逻辑组件类型包括:
① 顺序组件:用于THEN、WHEN;
② 分支组件:用于SWITCH、IF ...;
③ 循环组件:用于FOR、WHILE ...。 -
2、逻辑编排:通过EL表达式进行组件编排:
① 串行编排:THEN;
② 并行编排:WHEN;
③ 选择编排:SWITCH;
④ 条件编排:IF;
⑤ 循环编排:FOR、WHILE等等。
在这个项目里,我们的首要任务就是将EL表达式的操作符(Operator)进行建模,最终我们将EL表达式建模成了由ELNode组成的一棵树。
2、树形结构:
将LiteFlow的EL表达式进行文本拆解,我们其实能得到一个树形结构:
上面的树形结构来自于AST抽象语法树,使用AST explorer解析的完整的AST语法树如下所示:
{
"type": "Program",
"start": 0,
"end": 34,
"body": [
{
"type": "ExpressionStatement",
"start": 0,
"end": 34,
"expression": {
"type": "CallExpression",
"start": 0,
"end": 33,
"callee": {
"type": "Identifier",
"start": 0,
"end": 4,
"name": "THEN"
},
"arguments": [
{
"type": "Identifier",
"start": 8,
"end": 9,
"name": "a"
},
{
"type": "CallExpression",
"start": 13,
"end": 26,
"callee": {
"type": "Identifier",
"start": 13,
"end": 17,
"name": "WHEN"
},
"arguments": [
{
"type": "Identifier",
"start": 18,
"end": 19,
"name": "b"
},
{
"type": "Identifier",
"start": 21,
"end": 22,
"name": "c"
},
{
"type": "Identifier",
"start": 24,
"end": 25,
"name": "d"
}
],
"optional": false
},
{
"type": "Identifier",
"start": 30,
"end": 31,
"name": "e"
}
],
"optional": false
}
}
],
"sourceType": "module"
}
3、JSON表示
我们可以把这棵AST抽象语法树进行简化,得到一个简化版的JSON表示:
{
type: "THEN",
children: [
{ type: "NodeComponent", id: "a" },
{
type: "WHEN",
children: [
{ type: "NodeComponent", id: "b" },
{ type: "NodeComponent", id: "c" },
{ type: "NodeComponent", id: "d" },
],
},
{ type: "NodeComponent", id: "e" },
]
}
上面这个JSON数据表示,就是我们的目标格式,也是我们打算前后端进行数据交换的标准格式。
4、建立模型
经过以上步骤的分析,我们可以建立这么一个ELNode模型:
/**
* EL表达式的模型表示:数据结构本质上是一个树形结构。
* 例如一个串行编排(THEN):
* (1) EL表达式形式:THEN(a, b, c, d)
* (2) JSON表示形式:
* {
type: ConditionTypeEnum.THEN,
children: [
{ type: NodeTypeEnum.COMMON, id: 'a' },
{ type: NodeTypeEnum.COMMON, id: 'b' },
{ type: NodeTypeEnum.COMMON, id: 'c' },
{ type: NodeTypeEnum.COMMON, id: 'd' },
],
}
* (3) 通过ELNode节点模型表示为:
┌─────────────────┐
┌──▶│ NodeOperator │
│ └─────────────────┘
│ ┌─────────────────┐
├──▶│ NodeOperator │
┌─────────┐ ┌─────────────────┐ │ └─────────────────┘
│ Chain │───▶│ ThenOperator │──┤ ┌─────────────────┐
└─────────┘ └─────────────────┘ ├──▶│ NodeOperator │
│ └─────────────────┘
│ ┌─────────────────┐
└──▶│ NodeOperator │
└─────────────────┘
*/
export default abstract class ELNode {
// 节点类型:可以是编排类型,也可以是组件类型
public type: ConditionTypeEnum | NodeTypeEnum;
// 当前节点的子节点:编排类型有子节点,组件类型没有子节点
public children?: ELNode[];
// 当前节点的父节点
public parent?: ELNode;
// 判断类节点类型:主要用于SWITCH/IF/FOR/WHILE等编排类型
public condition?: ELNode;
// 组件节点的id
public id?: string;
// 编排节点的属性:可以设置id/tag等等
public properties?: Properties;
}
对于我们建立的ELNode模型,关键点有以下2个:
- 组合关系:一个EL表达式,最终是由ELNode组成的一棵树;
- 这棵树的根节点被我们定义为Chain,其他EL操作符(比如THEN/WHEN/SWITCH等关键字,也包括逻辑组件)都是树上的子节点;
- 需要注意的是,逻辑组件(NodeComponent)也被当做一种操作符(Operator),而且是这棵树的叶子结点。
- 继承关系:所有EL操作符(比如THEN/WHEN/SWITCH等),包括逻辑组件(NodeComponent),都继承自ELNode,同时有自己的特有属性和方法实现。
/**
* EL表达式各个操作符模型,继承关系为:
┌─────────────────┐
┌──▶│ ThenOperator │
│ └─────────────────┘
│ ┌─────────────────┐
├──▶│ WhenOperator │
│ └─────────────────┘
│ ┌─────────────────┐
├──▶│ SwitchOperator │
│ └─────────────────┘
┌──────────┐ │ ┌─────────────────┐
│ ELNode │────┼──▶│ IfOperator │
└──────────┘ │ └─────────────────┘
│ ┌─────────────────┐
├──▶│ ForOperator │
│ └─────────────────┘
│ ┌─────────────────┐
├──▶│ WhileOperator │
│ └─────────────────┘
│ ┌─────────────────┐
└──▶│ NodeOperator │
└─────────────────┘
*/
// 1. 顺序类
export { default as ThenOperator } from './then-operator';
export { default as WhenOperator } from './when-operator';
// 2. 分支类
export { default as SwitchOperator } from './switch-operator';
export { default as IfOperator } from './if-operator';
// 3. 循环类
export { default as ForOperator } from './for-operator';
export { default as WhileOperator } from './while-operator';
// 4. 节点类
export { default as NodeOperator } from './node-operator';
以上是我们《LiteFlow逻辑可视化编排设计与实现 02-数据模型篇(Model)》的内容,我们完成了EL表达式的操作符(Operator)的建模,最终我们将EL表达式建模成了由ELNode组成的一棵树。