1. 概述
本文将介绍 GoF 23 种设计模式中的行为型模式之一 —— Interpreter(解释器)模式。
我们会先讲清楚它的设计意图和适用场景,再通过 UML 图和代码实现一个贴近实际的案例。最后也会指出它的局限性,避免你在项目中“踩坑”。
这个模式的核心用途是:用面向对象的方式定义某种语言的语法,并提供一个解释器来执行它。
听起来有点抽象?别急,后面会用一个“类 SQL 查询”的例子让你秒懂。
2. Interpreter 模式详解
简单来说,Interpreter 模式允许你:
✅ 定义一门小型语言的语法规则
✅ 构建抽象语法树(AST)
✅ 通过解释器逐层执行,得到结果
典型应用场景包括:
- 自定义 DSL(领域特定语言)
- 简易表达式求值(比如数学公式、条件判断)
- SQL、JSON 等结构化语言的解析雏形
- IDE 中的语法高亮与语义分析(虽然实际实现更复杂)
⚠️ 但要注意:这个模式适合语法简单、变化不大的场景。一旦语法规则变得复杂,维护成本会急剧上升,不如直接上 ANTLR 这类专业工具。
所以它的定位很明确:简单粗暴地解决小问题,而不是造一个完整的编译器。
3. UML 结构图
图中主要有两个核心角色:
Context
:上下文,保存解释过程中共享的状态Expression
:表达式接口,所有语法节点都实现它
进一步拆解:
AbstractExpression
:声明interpret(Context)
方法,是所有表达式的顶层抽象TerminalExpression
:终结符表达式,不再向下递归,直接返回结果NonTerminalExpression
:非终结符表达式,内部包含其他表达式,会继续递归解释
✅ 关键点:NonTerminalExpression
本质上是一个 Composite(组合)模式 的实现 —— 它把多个表达式组合成树形结构。
客户端(Client)的任务就是构建这棵语法树(也就是一条“语句”),然后调用根节点的 interpret()
方法启动解释流程。
4. 代码实现
我们来实现一个极简版的“类 SQL 查询语言”,支持 SELECT ... FROM ... WHERE
结构。
4.1 表达式接口
所有表达式统一实现 Expression
接口:
List<String> interpret(Context ctx);
4.2 Select 表达式
对应 SQL 中的 SELECT column FROM ...
class Select implements Expression {
private String column;
private From from;
public Select(String column, From from) {
this.column = column;
this.from = from;
}
@Override
public List<String> interpret(Context ctx) {
ctx.setColumn(column);
return from.interpret(ctx);
}
}
✅ 这是一个典型的 NonTerminalExpression
:它设置上下文字段后,把解释权交给下一个表达式(From
)。
4.3 From 表达式
对应 FROM table [WHERE ...]
class From implements Expression {
private String table;
private Where where;
public From(String table) {
this(table, null);
}
public From(String table, Where where) {
this.table = table;
this.where = where;
}
@Override
public List<String> interpret(Context ctx) {
ctx.setTable(table);
if (where == null) {
return ctx.search(); // 终结:无 WHERE 条件
}
return where.interpret(ctx); // 继续解释 WHERE
}
}
⚠️ 注意:From
是个“灵活”的节点 —— 它可能是终结的(没有 WHERE),也可能是非终结的(有 WHERE)。
4.4 Where 表达式
对应 WHERE condition
class Where implements Expression {
private Predicate<String> filter;
public Where(Predicate<String> filter) {
this.filter = filter;
}
@Override
public List<String> interpret(Context ctx) {
ctx.setFilter(filter);
return ctx.search(); // 终结:执行查询
}
}
✅ Where
是 TerminalExpression
,设置完过滤条件后直接触发查询。
4.5 上下文(Context)
模拟数据库表数据,保存查询状态:
class Context {
private static Map<String, List<Row>> tables = new HashMap<>();
static {
List<Row> list = new ArrayList<>();
list.add(new Row("John", "Doe"));
list.add(new Row("Jan", "Kowalski"));
list.add(new Row("Dominic", "Doom"));
tables.put("people", list);
}
private String table;
private String column;
private Predicate<String> whereFilter;
public void setTable(String table) {
this.table = table;
}
public void setColumn(String column) {
this.column = column;
}
public void setFilter(Predicate<String> filter) {
this.whereFilter = filter;
}
List<String> search() {
Stream<Row> rowStream = tables.getOrDefault(table, Collections.emptyList()).stream();
Function<Row, Stream<String>> columnMapper = row -> {
if ("*".equals(column)) {
return Stream.of(row.toString());
} else if ("name".equals(column)) {
return Stream.of(row.getFirstName());
} else {
return Stream.empty();
}
};
List<String> result = rowStream
.flatMap(columnMapper)
.filter(s -> whereFilter == null || whereFilter.test(s))
.collect(Collectors.toList());
clear(); // 重置状态,避免影响下一次查询
return result;
}
private void clear() {
this.table = null;
this.column = null;
this.whereFilter = null;
}
}
✅ search()
是真正的“执行引擎”,根据当前上下文状态做数据筛选。
✅ clear()
很关键 —— 每次查询完重置状态,保证多次查询互不干扰。
5. 测试验证
写个 main
方法测试一下:
public class InterpreterDemo {
public static void main(String[] args) {
Context ctx = new Context();
// SELECT name FROM people
Expression query1 = new Select("name", new From("people"));
System.out.println(query1.interpret(ctx));
// 输出: [John, Jan, Dominic]
// SELECT * FROM people
Expression query2 = new Select("*", new From("people"));
System.out.println(query2.interpret(ctx));
// 输出: [John Doe, Jan Kowalski, Dominic Doom]
// SELECT name FROM people WHERE name LIKE 'd%'
Expression query3 = new Select("name",
new From("people",
new Where(name -> name.toLowerCase().startsWith("d"))));
System.out.println(query3.interpret(ctx));
// 输出: [Dominic]
}
}
输出完全符合预期 ✅
整个语法树的构建是“嵌套式”的,从外向内层层传递 Context
,最后由 Where
或 From
触发最终查询。
6. 缺点与局限
虽然实现看起来很优雅,但这个模式有明显短板:
❌ 语法复杂时难以维护
每新增一个关键字(如 ORDER BY
、LIMIT
),都要新增类和关联逻辑,代码量指数级增长。
❌ 性能一般
递归解释 + 多层对象调用,不如直接字符串解析或使用编译型 DSL 高效。
❌ 调试困难
语法树一旦出错,堆栈深,定位难。
所以建议:只在语法极其简单、且不常变更的场景下使用。否则,直接上 ANTLR、JavaCC 等专业工具更省心。
7. 总结
Interpreter 模式的价值在于:
✅ 用面向对象的方式建模语言结构,代码可读性强
✅ 适合实现小型 DSL 或配置规则引擎
✅ JDK 中也有它的影子,比如:
java.util.regex.Pattern
:正则表达式的解释java.text.Format
:格式化字符串的解析javax.el.ExpressionFactory
:EL 表达式解析
项目完整代码见:https://github.com/eugenp/tutorials/tree/master/patterns-modules/design-patterns-behavioral
一句话总结:语法简单用 Interpreter,复杂了就别硬扛,换专业工具才是正道。