1. 概述

本文将介绍 GoF 23 种设计模式中的行为型模式之一 —— Interpreter(解释器)模式

我们会先讲清楚它的设计意图和适用场景,再通过 UML 图和代码实现一个贴近实际的案例。最后也会指出它的局限性,避免你在项目中“踩坑”。

这个模式的核心用途是:用面向对象的方式定义某种语言的语法,并提供一个解释器来执行它

听起来有点抽象?别急,后面会用一个“类 SQL 查询”的例子让你秒懂。

2. Interpreter 模式详解

简单来说,Interpreter 模式允许你:

✅ 定义一门小型语言的语法规则
✅ 构建抽象语法树(AST)
✅ 通过解释器逐层执行,得到结果

典型应用场景包括:

  • 自定义 DSL(领域特定语言)
  • 简易表达式求值(比如数学公式、条件判断)
  • SQL、JSON 等结构化语言的解析雏形
  • IDE 中的语法高亮与语义分析(虽然实际实现更复杂)

⚠️ 但要注意:这个模式适合语法简单、变化不大的场景。一旦语法规则变得复杂,维护成本会急剧上升,不如直接上 ANTLR 这类专业工具。

所以它的定位很明确:简单粗暴地解决小问题,而不是造一个完整的编译器

3. UML 结构图

Interpreter

图中主要有两个核心角色:

  • 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(); // 终结:执行查询
    }
}

WhereTerminalExpression,设置完过滤条件后直接触发查询。

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,最后由 WhereFrom 触发最终查询。

6. 缺点与局限

虽然实现看起来很优雅,但这个模式有明显短板:

语法复杂时难以维护
每新增一个关键字(如 ORDER BYLIMIT),都要新增类和关联逻辑,代码量指数级增长。

性能一般
递归解释 + 多层对象调用,不如直接字符串解析或使用编译型 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,复杂了就别硬扛,换专业工具才是正道。