← 返回题目列表

解释器模式中的 Context 有什么作用?

高频 中等 第 7 / 25 题 更新于 2026/07/28
解释器模式Context上下文变量

简化版

Context 保存解释表达式所需的外部环境,例如变量值、用户信息、配置、函数表和中间状态。表达式描述规则,Context 提供数据,同一表达式在不同 Context 下可以得到不同结果。

详细版

Context 常保存:

  • 变量名到值的映射。
  • 当前用户属性。
  • 运行环境参数。
  • 函数或操作符注册表。
  • 解释过程中的临时数据。

例如表达式 age > 18 本身不包含具体用户年龄。解释时从 Context 取出 age,再和 18 比较。

Context 的好处是让表达式对象和具体数据解耦。表达式可以复用,Context 可以替换。

设计 Context 时要注意线程安全、只读性、变量缺失处理和类型转换。

完整版教学

一、表达式和数据为什么要分开

表达式是一条规则,Context 是规则运行时的数据。

同一条规则 score >= 60,在学生 A 的 Context 中可能为 true,在学生 B 的 Context 中可能为 false。规则不变,数据变了,结果就变。

这就是 Context 的价值。

二、Context 的常见实现

简单实现可以是 Map:

class Context {
    private Map<String, Object> variables;

    public Object get(String name) {
        return variables.get(name);
    }
}

复杂实现可能包含类型系统、函数调用、权限信息、时间、区域、租户信息等。

三、Context 设计要注意什么

变量缺失时应该怎么处理?返回 null、抛异常,还是使用默认值?不同业务选择不同。

类型转换也很重要。字符串 "18" 和数字 18 是否能比较?布尔值如何解析?这些规则如果不统一,表达式结果会不稳定。

四、常见误区与工程判断

不要让表达式节点到处访问数据库或外部服务。解释器模式中的 Context 应尽量提供解释所需数据,表达式节点专注解释逻辑。

工程中如果表达式解释过程中不断远程调用,性能和可预测性都会很差。更好的方式是提前准备上下文数据。

五、Context 不应该变成万能对象

Context 的作用是提供解释过程中需要的外部信息,例如变量值、环境参数、函数表、运行时配置。它不是用来塞所有业务服务的容器。如果 Context 过大,Expression 会对各种外部对象产生强依赖,解释器会变得难测试、难复用。

设计 Context 时可以遵循两个原则。第一,只放解释表达式真正需要的信息,不放和语法执行无关的业务流程。第二,尽量让 Context 的接口表达语义,比如 getVariable("age")hasPermission("admin"),而不是把内部 Map 直接暴露出去让表达式随意操作。

如果表达式执行需要修改上下文,也要明确副作用边界。纯规则判断最好让解释过程无副作用;如果是脚本执行类 DSL,需要修改状态,就要考虑执行顺序、错误回滚、并发隔离等问题。面试里能说到这点,说明你理解的是运行模型而不是类名。

六、用工程约束检验答案

同一 AST price * rate 可分别在 {price:100, rate:0.8}{price:200, rate:0.9} 下得到 80 与 180。表达式树不变,Context 提供本次求值数据;这也是 AST 可缓存而请求上下文不能错误共享的原因。

检查项核心判断工程含义
变量表name -> typed value查找 price、rate
环境能力时区、函数注册表必须按白名单暴露
执行状态错误、预算、调用深度防止无限或过量计算

把关键关系压缩成一条可复述的路径:

immutable AST
   + request Context A -> 80
   + request Context B -> 180
Context 生命周期通常不跨请求共享

Context 应是最小、明确、可测试的运行环境,不能演化成让所有表达式随意访问服务的万能容器。

落地前可以再按下面 3 步复核:

  1. 先说明“变量表”的核心机制:name -> typed value;再交代边界:查找 price、rate。
  2. 接着分析“环境能力”:时区、函数注册表;不能遗漏对应代价或结果:必须按白名单暴露。
  3. 最后用“执行状态”检查方案:错误、预算、调用深度;验收时确认防止无限或过量计算。

这三项构成完整判断链:先讲清变量表,再说明环境能力,最后用执行状态检验实现是否越界。

面试中若能给出违反“防止无限或过量计算”的反例,再说明修正办法,答案就从模式定义落到了可验证的工程决策。

七、常见误区与追问

  • 误区:只看到“变量表”就认为方案成立。 必须同时说明核心机制“name -> typed value”和工程边界“查找 price、rate”。
  • 误区:把“环境能力”当成无条件结论。 只有在“时区、函数注册表”成立时,才能据此讨论“必须按白名单暴露”。
  • 追问:Context 可以可变吗? 可以,但副作用会降低可预测性;纯求值优先使用只读上下文。
  • 追问:变量缺失返回 null 吗? 应采用明确的错误、Optional 或领域默认值,避免隐式空值传播。
  • 追问:能把数据库连接放进去吗? 技术上能,但会让解释产生隐蔽 I/O,通常应先准备数据或限制能力。
  • 追问:多线程能共享 Context 吗? 只有不可变且无请求状态时才安全。
  • 追问:AST 缓存要连 Context 一起缓存吗? 通常只缓存 AST,Context 应按本次请求创建。

八、加强记忆

记忆时抓住这条主线:Context 保存解释所需外部数据;表达式描述规则,Context 提供变量值;同一表达式可在不同 Context 下复用;Context 要处理变量缺失和类型转换。面试回答先给出模式意图,再用调用链或数据流说明角色协作,最后主动交代适用边界与工程代价。