解释器模式(Interpreter):把"语法规则"变成类
更新时间:2026-08-25。本文按"先看不用模式的困境 → 再用模式重构 → 类图对比分析"的结构,论述解释器模式。可运行代码:demos/design-patterns/interpreter。
一、问题场景:需要解释一门"小语言"
配置表达式(price > 100 && vip)、规则引擎(age >= 18 && !blocked)、SQL 子集、布尔查询。语言有固定语法(终结符 + 非终结符),且语法可能扩展。
二、不使用模式:if/else 手写解析与求值
cpp
// 手写解析字符串 + 求值
bool eval(const std::string& expr, const Context& ctx) {
// 拆 token、找 && || !、比较数字、查上下文……
// 语法规则混在解析代码里,加一个运算符 = 改解析逻辑
}| 缺陷 | 说明 |
|---|---|
| 语法规则散落 | 解析与求值逻辑纠缠,难读难改 |
| 违反 OCP | 加语法元素 = 改解析函数 |
| 无法复用 | 表达式无法作为对象被组合/缓存/遍历 |
| 难以优化 | 无法建语法树做公共子表达式消除 |
不使用模式的类图

三、使用模式:每个语法元素一个表达式类
解释器模式把语法规则建模为表达式类的组合:终结符表达式(变量、常量)与非终结符表达式(&&、||、!、比较)都实现统一 interpret(ctx) 接口,非终结符由子表达式递归组合——语法树即对象树,求值 = 递归解释。
cpp
// interpreter.h
class Expression { // 抽象表达式
public:
virtual bool interpret(const Context& ctx) const = 0;
virtual ~Expression() = default;
};
class VarExpr : public Expression { // 终结符:变量
public:
explicit VarExpr(std::string name) : name_(std::move(name)) {}
bool interpret(const Context& ctx) const override { return ctx.get(name_); }
private:
std::string name_;
};
class AndExpr : public Expression { // 非终结符:&&
public:
AndExpr(std::shared_ptr<Expression> l, std::shared_ptr<Expression> r)
: l_(std::move(l)), r_(std::move(r)) {}
bool interpret(const Context& ctx) const override {
return l_->interpret(ctx) && r_->interpret(ctx); // 递归
}
private:
std::shared_ptr<Expression> l_, r_;
};
class NotExpr : public Expression {
public:
explicit NotExpr(std::shared_ptr<Expression> e) : e_(std::move(e)) {}
bool interpret(const Context& ctx) const override { return !e_->interpret(ctx); }
private:
std::shared_ptr<Expression> e_;
};
// 组合语法树(a && !b)——表达式是对象,可复用/缓存/遍历
auto expr = std::make_shared<AndExpr>(
std::make_shared<VarExpr>("a"),
std::make_shared<NotExpr>(std::make_shared<VarExpr>("b")));
Context ctx{ {"a", true}, {"b", false} };
expr->interpret(ctx); // true使用模式的类图

四、类图对比与分析
| 维度 | 不使用(手写解析) | 使用解释器 |
|---|---|---|
| 语法规则 | 藏在解析代码里 | 显式化为表达式类 |
| 加语法元素 | 改解析函数 | 新增表达式类 |
| 表达式复用 | 不可复用 | 对象可组合/缓存/遍历 |
| 可优化性 | 无语法树 | 可建树做优化(公共子表达式) |
| 类数量 | 无 | 每语法元素一个类(N 个类) |
分析结论:解释器把"语法"变成显式的类层级,代价是类数量多、递归解释有调用开销。适合语法小且固定的语言(表达式、规则、过滤条件);语法大而复杂(如完整语言)应改用真正的解析器生成器(如 ANTLR)。本仓库性能视角:递归解释在长表达式上是热点,可先构建语法树再解释(与解释器天然匹配)。
五、适用边界
| 适用 | 不适用 |
|---|---|
| 语法简单、规则相对稳定 | 语法复杂/庞大(用解析器生成器) |
| 需要表达式对象化(复用/缓存/组合) | 一次性简单判断(if 直接写) |
| 语法预期扩展(加运算符/关键字) | 性能极端敏感(考虑编译到闭包/字节码) |
工程提示:解释器模式常与组合模式(语法树是树形结构)、访问者模式(对语法树做打印/优化/求值)搭配。工业实现中,"解释"常被"编译"替代——把表达式预编译为闭包或字节码,运行时直接执行,性能提升一个量级。
一句话总结
解释器模式把语法规则建模为表达式类层级,非终结符递归组合子表达式、求值即递归 interpret——语法显式化、加语法元素只加类(OCP)、表达式可对象化复用;适合小而稳定的领域语言,大语法应用解析器生成器或编译为闭包/字节码。