1175 字
约 3 分钟
1
DSL 是什么?如何定义规则并映射成 Java 对象
DSL 是什么?如何定义规则并映射成 Java 对象
在很多规则引擎、工作流、搜索条件、配置系统中,经常会看到 DSL。
DSL 全称:
Domain Specific Language
中文一般叫:
领域特定语言
它本质上就是:
为某一个特定业务领域设计的一套“小语言”。
比如我们设计一个优惠规则 DSL:
age > 18 && vip == true
它表达的业务含义就是:
年龄大于 18,并且是 VIP 用户
相比直接把规则写死在 Java:
if (user.getAge() > 18 && user.isVip()) {
// ...
}
DSL 最大的价值是:
规则和代码解耦。
以后修改规则时,不一定需要重新修改 Java 代码。
一、DSL 本质是什么?
一个 DSL 通常由三部分组成:
语法
语义
执行逻辑
例如:
age >= 18
这里:
age
是字段。
>=
是操作符。
18
是值。
因此可以把一条 DSL 规则抽象成:
字段 + 操作符 + 值
Java 对象可以定义成:
public class Rule {
private String field;
private String operator;
private Object value;
}
DSL:
age >= 18
解析以后就是:
Rule rule = new Rule();
rule.setField("age");
rule.setOperator(">=");
rule.setValue(18);
这就是最简单的:
DSL -> Java Object
二、DSL 如何定义规则?
稍微复杂一点的 DSL:
age >= 18 && level == "VIP"
它实际上包含两个规则:
age >= 18
和:
level == "VIP"
中间通过:
&&
连接。
因此 DSL 可以抽象成一棵树:
AND
/ \
age >= 18 level == VIP
对应 Java 对象可以设计为:
public interface Expression {
}
普通条件:
public class Condition implements Expression {
private String field;
private String operator;
private Object value;
}
逻辑条件:
public class LogicExpression implements Expression {
private String logic;
private Expression left;
private Expression right;
}
于是:
age >= 18 && level == "VIP"
最终会变成:
LogicExpression
├── logic = AND
├── left
│ ├── field = age
│ ├── operator = >=
│ └── value = 18
└── right
├── field = level
├── operator = ==
└── value = VIP
这就是 DSL 最核心的思想:
DSL 最终通常都会被解析成一棵表达式树。
这棵树也经常叫:
AST
全称:
Abstract Syntax Tree
即:
抽象语法树
三、DSL 如何映射成 Java 对象?
整体流程通常是:
DSL 字符串
↓
词法解析
↓
语法解析
↓
AST
↓
Java 对象
↓
执行
例如 DSL:
age >= 18 && vip == true
第一步拆成 Token:
age
>=
18
&&
vip
==
true
然后识别结构:
Condition
AND
Condition
最后生成 Java 对象:
LogicExpression root = new LogicExpression();
root.setLogic("AND");
root.setLeft(
new Condition("age", ">=", 18)
);
root.setRight(
new Condition("vip", "==", true)
);
后面的规则执行器只需要处理 Java 对象:
boolean execute(Expression expression) {
if (expression instanceof Condition condition) {
return executeCondition(condition);
}
if (expression instanceof LogicExpression logic) {
boolean left = execute(logic.getLeft());
boolean right = execute(logic.getRight());
if ("AND".equals(logic.getLogic())) {
return left && right;
}
if ("OR".equals(logic.getLogic())) {
return left || right;
}
}
return false;
}
因此真正执行规则的时候,实际上已经不关心 DSL 字符串了。
执行的是:
DSL
↓
AST
↓
Java Object
↓
Rule Executor
四、实际项目里怎么做?
简单 DSL 可以自己解析:
String rule = "age >= 18";
String[] parts = rule.split(" ");
String field = parts[0];
String operator = parts[1];
String value = parts[2];
但是 DSL 一旦支持:
&&
||
()
>
<
>=
<=
==
!=
IN
NOT IN
例如:
(age >= 18 && vip == true)
||
(level == "SVIP" && score > 1000)
就不建议手写字符串解析了。
通常会使用:
ANTLR
来定义 DSL Grammar。
例如:
expr
: expr AND expr
| expr OR expr
| condition
| '(' expr ')'
;
condition
: IDENTIFIER OPERATOR VALUE
;
ANTLR 会根据 Grammar 帮我们生成 Parser。
整体结构就变成:
DSL
↓
ANTLR Lexer
↓
ANTLR Parser
↓
ParseTree / AST
↓
Java Object
↓
业务执行器
五、总结
DSL 可以简单理解成:
用一套接近业务语言的语法描述业务规则。
例如:
age >= 18 && vip == true
程序内部不会直接执行这段字符串,而是:
DSL
↓
解析
↓
AST
↓
Java Object
↓
执行器
所以设计 DSL 时,真正重要的不是字符串怎么写,而是先设计好:
Rule
Condition
Expression
Operator
LogicExpression
这些核心领域对象。
因为:
DSL 只是外部表现形式,AST 和 Java 对象才是程序真正处理的规则模型。
DSL 是什么?如何定义规则并映射成 Java 对象
http://www.clxhxhhr.top/posts/640/ 评论
0 条
还没有评论,先写一条吧。