解释器模式
从权限规则配置说起
HR 系统需要支持灵活的权限规则,例如:"isLoggedIn AND (hasRole('ADMIN') OR hasPermission('EDIT'))"。这类规则随业务频繁变化,每次修改都要找开发者改 if-else、走测试、走发布流程——效率极低。
解释器模式的解法是把规则定义成一种"小语言":为每种表达式(AND、OR、常量)定义一个类,通过组合这些类构建可执行的语法树,规则字符串变成可配置的数据而非硬编码逻辑,业务人员自己就能维护规则。
🔍 定义
解释器模式(Interpreter)为一个语言定义文法,并建立一个解释器来解释该语言中的句子。每条文法规则对应一个类,通过组合这些类构建语法树(AST),最后递归解释执行。
⚠️ 不使用解释器存在的问题
权限系统需要支持灵活的规则表达式,如 "isLoggedIn AND (hasRole('ADMIN') OR hasPermission('EDIT'))" ——如果用字符串拼接硬编码处理,逻辑复杂且不可扩展:
| InterpreterBadExample.java |
|---|
| package com.example.behavioral.interpreter;
/**
* 解释器模式 - 反例
* 问题:解析逻辑用字符串处理硬编码,扩展新语法需要修改 evaluate 方法
*/
public class InterpreterBadExample {
public static void main(String[] args) {
InterpreterBadExample demo = new InterpreterBadExample();
// ❌ 只支持简单的 AND/OR,且逻辑全硬编码
System.out.println(demo.evaluate("true AND false")); // false
System.out.println(demo.evaluate("true OR false")); // true
System.out.println(demo.evaluate("true AND true")); // true
// 支持 NOT?要修改 evaluate 方法 ❌
}
// ❌ 字符串处理硬编码,扩展性差
public boolean evaluate(String expression) {
String[] parts = expression.split(" ");
if (parts.length == 3) {
boolean left = Boolean.parseBoolean(parts[0]);
String op = parts[1];
boolean right = Boolean.parseBoolean(parts[2]);
if ("AND".equals(op)) return left && right;
if ("OR".equals(op)) return left || right;
}
if (parts.length == 1) {
return Boolean.parseBoolean(parts[0]);
}
throw new IllegalArgumentException("不支持的表达式: " + expression);
}
}
|
🏗️ 设计模式结构说明
%%{init: {'themeVariables': {'noteBkgColor': 'transparent', 'noteBorderColor': '#768390'}}}%%
classDiagram
classDef default fill:transparent,stroke:#768390
class Expression {
<<interface>>
+interpret(ctx) boolean
}
class VariableExpression {
-name: String
+interpret(ctx) boolean
}
class AndExpression {
-left: Expression
-right: Expression
+interpret(ctx) boolean
}
class OrExpression {
-left: Expression
-right: Expression
+interpret(ctx) boolean
}
class NotExpression {
-expr: Expression
+interpret(ctx) boolean
}
Expression <|.. VariableExpression
Expression <|.. AndExpression
Expression <|.. OrExpression
Expression <|.. NotExpression
AndExpression o--> Expression
OrExpression o--> Expression
NotExpression o--> Expression
note for Expression "抽象表达式(AbstractExpression)"
note for VariableExpression "终结符(Terminal)"
note for AndExpression "非终结符(Nonterminal)"
每种文法规则对应一个类,组合后形成语法树,interpret() 递归求值。
💻 设计模式举例说明
| InterpreterExample.java |
|---|
| package com.example.behavioral.interpreter;
import java.util.HashMap;
import java.util.Map;
/**
* 解释器模式 - 正例
* 每种语法规则封装为一个 Expression 类,新增语法只需添加新类
*/
public class InterpreterExample {
public static void main(String[] args) {
// 构建表达式:(isLoggedIn AND isAdmin) OR isSuperUser
Map<String, Boolean> context = new HashMap<>();
context.put("isLoggedIn", true);
context.put("isAdmin", false);
context.put("isSuperUser", true);
Expression isLoggedIn = new VariableExpression("isLoggedIn");
Expression isAdmin = new VariableExpression("isAdmin");
Expression isSuperUser = new VariableExpression("isSuperUser");
// ✅ 组合表达式:(isLoggedIn AND isAdmin) OR isSuperUser
Expression rule = new OrExpression(
new AndExpression(isLoggedIn, isAdmin),
isSuperUser
);
System.out.println("有权限访问?" + rule.interpret(context)); // true
// ✅ NOT 语法:新增一个 NotExpression 类即可,不改其他代码
Expression notAdmin = new NotExpression(isAdmin);
System.out.println("非管理员?" + notAdmin.interpret(context)); // true
}
}
// 抽象表达式
interface Expression {
boolean interpret(Map<String, Boolean> context);
}
// 终结符:变量(从上下文中读取值)
class VariableExpression implements Expression {
private final String name;
public VariableExpression(String name) { this.name = name; }
@Override
public boolean interpret(Map<String, Boolean> context) {
return context.getOrDefault(name, false);
}
}
// 非终结符:AND
class AndExpression implements Expression {
private final Expression left;
private final Expression right;
public AndExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public boolean interpret(Map<String, Boolean> context) {
return left.interpret(context) && right.interpret(context);
}
}
// 非终结符:OR
class OrExpression implements Expression {
private final Expression left;
private final Expression right;
public OrExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public boolean interpret(Map<String, Boolean> context) {
return left.interpret(context) || right.interpret(context);
}
}
// ✅ 非终结符:NOT(新增语法,只需一个新类)
class NotExpression implements Expression {
private final Expression expression;
public NotExpression(Expression expression) { this.expression = expression; }
@Override
public boolean interpret(Map<String, Boolean> context) {
return !expression.interpret(context);
}
}
|
⚖️ 优缺点
优点:
- 文法规则可扩展,每条规则对应一个类,符合**开闭原则**
- 组合构建 AST 的方式灵活且直观
- 易于实现简单语言的解释器
缺点:
- 文法规则复杂时,类数量快速增长
- 对于复杂语言(如完整编程语言),手写解释器维护成本极高
- 递归解释大型 AST 时可能有性能问题
使用场景限制
解释器模式适合**语法简单**的场景(SQL WHERE 片段、规则表达式、数学公式)。语法复杂时应使用专业的解析器生成工具(如 ANTLR、JavaCC),而非手写解释器。
🔗 与其它模式的关系
- 组合模式:解释器模式常用**组合模式**构建抽象语法树(AST)——终结符表达式是叶节点,非终结符表达式是复合节点,二者共同实现
Expression 接口
- 访问者模式:遍历并操作 AST 各节点时可引入**访问者模式**,将操作逻辑(如求值、打印、类型检查)从表达式类中解耦
- 策略模式:当只有单条规则需要动态切换时,**策略模式**是更轻量的替代方案;解释器适合规则组合与语法文法场景
- 模板方法模式:解释器的
interpret() 方法常作为模板方法的一个具体步骤,父类定义解释流程骨架
🗂️ 应用场景
- 简单 DSL(领域专用语言)的解析与执行
- 规则引擎(布尔表达式、权限规则)
- 数学表达式求值
- Spring Expression Language(SpEL)背后使用了解释器模式
- SQL WHERE 条件的简化解析
🏭 工业视角
告警规则引擎:解释器模式的典型工程场景
解释器模式的核心思想是**为文法中的每条规则定义一个类**,通过组合构建语法树,递归调用 interpret() 求值。监控系统的自定义告警规则是最贴近真实工程的例子:
| 告警规则解释器(每个运算符对应一个 Expression 类) |
|---|
| // 规则表达式:api_error_per_minute > 100 && api_count_per_minute > 10000
public class GreaterExpression implements Expression {
private String key;
private long value;
public GreaterExpression(String strExpr) {
// 解析 "api_error_per_minute > 100"
String[] parts = strExpr.trim().split("\\s+");
this.key = parts[0];
this.value = Long.parseLong(parts[2]);
}
@Override
public boolean interpret(Map<String, Long> stats) {
return stats.getOrDefault(key, 0L) > value;
}
}
// AndExpression 组合多个子 Expression,全部为真才返回 true
// OrExpression 组合多个子 Expression,任意为真就返回 true
// 将规则字符串解析为组合对象树后,一次 interpret(apiStats) 完成整个规则求值
|
把复杂的解析逻辑分散到多个职责单一的小类,避免了一个巨大的 if-else 解析函数——这正是解释器模式相比朴素实现的优势所在。
工程中复杂语法不要手写解释器
解释器模式只适合**语法规则简单**的场景(运算符种类 ≤ 10、优先级固定)。语法一旦变复杂,手写解释器的维护成本会急剧上升。
复杂语法用专业工具,不要手写
面对完整的 SQL 解析、编程语言编译器、复杂 DSL 时,应使用成熟的解析器生成工具
(如 ANTLR、JavaCC),而非手写解释器。手写解释器只适合规则运算符极少的
简单场景(告警规则引擎、权限表达式、数学公式计算器)。
解释器模式的工业落地形态
Spring Expression Language(SpEL)、MyBatis 动态 SQL(<if>、<choose>)都体现了
解释器模式的思想——把"用户输入的表达式字符串"翻译成可执行的对象树,
在运行时根据上下文数据动态求值。