解释器模式(Interpreter Pattern)
解释器模式(Interpreter Pattern)
一、核心思想
解释器模式为某种语言定义一套语法规则,然后用对象把这些规则表示成一棵抽象语法树(AST),再通过递归遍历这棵树来"解释"并执行表达式。
通俗来说:把"解释一句话"这件事拆成"每个语法成分各管一段",然后用树形组合把它们拼起来。
生活中的类比
想象你在计算 3 + (2 * 5):
- 你先看最外层——是加法,左边是
3,右边是(2 * 5) - 右边的括号里是乘法,左边是
2,右边是5 - 你先算最底层(叶子节点)的数字,再逐层往上算
这棵树就是 AST,你计算的过程就是"解释"这棵树。
二、模式结构
| 角色 | 职责 | Go 中的体现 |
|---|---|---|
| AbstractExpression(抽象表达式) | 声明 Interpret() 方法 |
interface |
| TerminalExpression(终结符表达式) | 不可再分的叶子节点(数字、变量、关键字) | 实现 interface 的 struct |
| NonterminalExpression(非终结符表达式) | 可组合的内部节点(加法、与/或逻辑) | 持有子 Expression 的 struct |
| Context(上下文) | 存储解释过程中需要的全局信息(如变量映射) | map 或自定义 struct |
| Client(客户端) | 构造 AST 并调用 Interpret() |
main 或调用方 |
类关系示意
┌─────────────────────┐
│ Expression (interface) │
│ + Interpret(ctx) → int │
└──────────┬──────────┘
│
┌─────────────┼──────────────┐
│ │ │
Terminal Add(非终结) Subtract(非终结)
(Number) left, right left, right
三、关键设计要点
1. 递归组合是灵魂
每个非终结符表达式内部持有子 Expression,Interpret() 调用时递归向下求值。这就是组合模式的天然契合点——AST 本身就是一棵递归树。
2. 终结符 vs 非终结符
- 终结符:表达式树的叶子,不可再拆。如数字字面量
42、布尔值true、变量引用x - 非终结符:树的内部节点,组合多个子表达式。如加法
a + b、逻辑与a AND b
3. Context 的作用
Context 负责传递"环境信息",最常见的是变量名→值的映射。终结符表达式从 Context 中查找变量值,非终结符表达式把 Context 往下传给子表达式。
4. 扩展性优势
新增一种运算符(如乘法)只需要新增一个 struct 实现 Expression 接口,完全不需要修改现有代码——符合开闭原则。
四、适用场景
| 场景 | 说明 |
|---|---|
| 数学表达式求值 | 计算器、公式引擎 |
| 规则引擎 | "金额 > 100 AND 用户等级 = VIP" 这类业务规则 |
| DSL 解析 | 领域特定语言的简单解释器 |
| 字符串模式匹配 | 布尔搜索 "hello AND (world OR earth)" |
| SQL/正则引擎 | 基于语法的查询解析(工业级实现更复杂) |
注意:解释器模式不适合复杂语法——每条规则一个类,类数量会爆炸。真正的编译器/解释器会用更高效的方式(如编译成字节码再执行)。
五、与组合模式的关系
解释器模式可以看作组合模式 + 递归求值:
- 组合模式关注的是树形结构的统一操作(如文件系统遍历)
- 解释器模式关注的是按语法规则递归求值
两者结构几乎一样,区别在于解释器给每个节点赋予了"解释"的语义。
六、Go 实现示例
下面的示例实现一个支持加减乘除四则运算的表达式解释器。
package main
import (
"fmt"
"strings"
)
// ============================================================
// 抽象表达式接口
// ============================================================
// Expression 是所有表达式节点的抽象接口。
// Context 是变量名→值的映射表。
type Expression interface {
Interpret(ctx map[string]float64) float64
String() string // 打印表达式结构,方便调试
}
// ============================================================
// 终结符表达式
// ============================================================
// Number 表示一个数字字面量,如 3.14
type Number struct {
Value float64
}
func (n *Number) Interpret(_ map[string]float64) float64 {
return n.Value
}
func (n *Number) String() string {
return fmt.Sprintf("%g", n.Value)
}
// Variable 表示一个变量引用,如 x
type Variable struct {
Name string
}
func (v *Variable) Interpret(ctx map[string]float64) float64 {
val, ok := ctx[v.Name]
if !ok {
panic(fmt.Sprintf("未定义的变量: %s", v.Name))
}
return val
}
func (v *Variable) String() string {
return v.Name
}
// ============================================================
// 非终结符表达式 —— 四则运算
// ============================================================
// Add 加法表达式: left + right
type Add struct {
Left, Right Expression
}
func (a *Add) Interpret(ctx map[string]float64) float64 {
return a.Left.Interpret(ctx) + a.Right.Interpret(ctx)
}
func (a *Add) String() string {
return fmt.Sprintf("(%s + %s)", a.Left.String(), a.Right.String())
}
// Subtract 减法表达式: left - right
type Subtract struct {
Left, Right Expression
}
func (s *Subtract) Interpret(ctx map[string]float64) float64 {
return s.Left.Interpret(ctx) - s.Right.Interpret(ctx)
}
func (s *Subtract) String() string {
return fmt.Sprintf("(%s - %s)", s.Left.String(), s.Right.String())
}
// Multiply 乘法表达式: left * right
type Multiply struct {
Left, Right Expression
}
func (m *Multiply) Interpret(ctx map[string]float64) float64 {
return m.Left.Interpret(ctx) * m.Right.Interpret(ctx)
}
func (m *Multiply) String() string {
return fmt.Sprintf("(%s * %s)", m.Left.String(), m.Right.String())
}
// Divide 除法表达式: left / right
type Divide struct {
Left, Right Expression
}
func (d *Divide) Interpret(ctx map[string]float64) float64 {
divisor := d.Right.Interpret(ctx)
if divisor == 0 {
panic("除数不能为零")
}
return d.Left.Interpret(ctx) / divisor
}
func (d *Divide) String() string {
return fmt.Sprintf("(%s / %s)", d.Left.String(), d.Right.String())
}
// ============================================================
// 简易解析器 —— 将字符串解析成 AST
// 本例实现一个递归下降解析器,支持 +、-、*、/、括号、数字
// 文法:
// expr → term (('+' | '-') term)*
// term → factor (('*' | '/') factor)*
// factor → NUMBER | VARIABLE | '(' expr ')'
// ============================================================
// Parser 递归下降解析器
type Parser struct {
tokens []string
pos int
}
// Tokenize 将输入字符串分词
func Tokenize(input string) []string {
// 在运算符和括号前后加空格,再按空白分割
input = strings.ReplaceAll(input, "+", " + ")
input = strings.ReplaceAll(input, "-", " - ")
input = strings.ReplaceAll(input, "*", " * ")
input = strings.ReplaceAll(input, "/", " / ")
input = strings.ReplaceAll(input, "(", " ( ")
input = strings.ReplaceAll(input, ")", " ) ")
return strings.Fields(input)
}
// NewParser 创建解析器
func NewParser(input string) *Parser {
return &Parser{tokens: Tokenize(input)}
}
// peek 查看当前 token
func (p *Parser) peek() string {
if p.pos >= len(p.tokens) {
return ""
}
return p.tokens[p.pos]
}
// consume 消费当前 token
func (p *Parser) consume() string {
tok := p.peek()
p.pos++
return tok
}
// Parse 开始解析,返回表达式 AST 根节点
func (p *Parser) Parse() Expression {
return p.parseExpr()
}
// parseExpr 解析加减法: expr → term (('+' | '-') term)*
func (p *Parser) parseExpr() Expression {
expr := p.parseTerm()
for {
op := p.peek()
if op == "+" {
p.consume()
right := p.parseTerm()
expr = &Add{Left: expr, Right: right}
} else if op == "-" {
p.consume()
right := p.parseTerm()
expr = &Subtract{Left: expr, Right: right}
} else {
break
}
}
return expr
}
// parseTerm 解析乘除法: term → factor (('*' | '/') factor)*
func (p *Parser) parseTerm() Expression {
expr := p.parseFactor()
for {
op := p.peek()
if op == "*" {
p.consume()
right := p.parseFactor()
expr = &Multiply{Left: expr, Right: right}
} else if op == "/" {
p.consume()
right := p.parseFactor()
expr = &Divide{Left: expr, Right: right}
} else {
break
}
}
return expr
}
// parseFactor 解析因子: NUMBER | VARIABLE | '(' expr ')'
func (p *Parser) parseFactor() Expression {
tok := p.peek()
if tok == "(" {
p.consume() // 消费 '('
expr := p.parseExpr() // 递归解析括号内表达式
if p.peek() == ")" {
p.consume() // 消费 ')'
}
return expr
}
// 尝试解析为数字
var num float64
if n, err := fmt.Sscanf(tok, "%g", &num); err == nil && n == 1 {
p.consume()
return &Number{Value: num}
}
// 否则视为变量
p.consume()
return &Variable{Name: tok}
}
// ============================================================
// 客户端使用
// ============================================================
func main() {
// 场景1:纯数字表达式
// 表达式: 3 + 5 * 2 = 13
p1 := NewParser("3 + 5 * 2")
ast1 := p1.Parse()
ctx := map[string]float64{}
fmt.Printf("表达式: %s\n", ast1.String())
fmt.Printf("结果: %g\n\n", ast1.Interpret(ctx))
// 场景2:带括号的表达式
// 表达式: (3 + 5) * 2 = 16
p2 := NewParser("(3 + 5) * 2")
ast2 := p2.Parse()
fmt.Printf("表达式: %s\n", ast2.String())
fmt.Printf("结果: %g\n\n", ast2.Interpret(ctx))
// 场景3:带变量的表达式
// 表达式: x * y + 10,其中 x=3, y=4 → 22
p3 := NewParser("x * y + 10")
ast3 := p3.Parse()
ctx3 := map[string]float64{"x": 3, "y": 4}
fmt.Printf("表达式: %s\n", ast3.String())
fmt.Printf("变量: x=3, y=4\n")
fmt.Printf("结果: %g\n\n", ast3.Interpret(ctx3))
// 场景4:复杂混合表达式
// 表达式: (x + y) * (x - y) / 2,其中 x=10, y=3 → 45.5
p4 := NewParser("(x + y) * (x - y) / 2")
ast4 := p4.Parse()
ctx4 := map[string]float64{"x": 10, "y": 3}
fmt.Printf("表达式: %s\n", ast4.String())
fmt.Printf("变量: x=10, y=3\n")
fmt.Printf("结果: %g\n", ast4.Interpret(ctx4))
}
运行结果
表达式: (3 + (5 * 2))
结果: 13
表达式: ((3 + 5) * 2)
结果: 16
表达式: ((x * y) + 10)
变量: x=3, y=4
结果: 22
表达式: (((x + y) * (x - y)) / 2)
变量: x=10, y=3
结果: 45.5
七、优缺点总结
优点
- 易于扩展:新增运算符只需新增一个 struct,不改已有代码
- 语法与解释分离:语法规则用类型表示,解释逻辑在
Interpret()中,清晰分离 - 天然递归:AST 的树形结构与递归求值完美契合
缺点
- 类爆炸:每条语法规则一个 struct,复杂语法导致大量类型
- 执行效率低:递归遍历 + 接口调用开销大,不适合高频执行
- 复杂语法维护难:真正复杂的语法更适合用 parser generator 生成
八、与其他模式的关系
| 对比项 | 区别 |
|---|---|
| vs 组合模式 | 结构相同,但解释器给节点赋予了"求值"语义 |
| vs 策略模式 | 策略是同一层面的替换选择;解释器是嵌套组合的语法树 |
| vs 访问者模式 | 访问者把操作从对象结构中剥离;解释器把操作内聚在每个节点 |
九、学习小结
解释器模式是 GoF 23 种设计模式中使用频率最低的模式之一,但理解它对学习编译原理和 DSL 设计很有价值。本次实现了一个完整的四则运算表达式解释器,包含:
- 抽象表达式接口(
Expression) - 终结符表达式(
Number、Variable) - 非终结符表达式(
Add、Subtract、Multiply、Divide) - 递归下降解析器(
Parser)将字符串解析为 AST - Context 变量映射(
map[string]float64)

浙公网安备 33010602011771号