解释器模式:用来在程序里创建针对一个特点领域语言的解释器,用于处理解释领域语言中的语句。该模式定义了领域语言的抽象语法树以及用示来解释语法树的解释器。
使用解释器模式的真实场景:
-
处理配置文件
许多应用程序使用配置文件来指定应用程序的行为方式。这些配置文件可以用 YAML 或 JSON 等 DSL 编写。解释器可用于解析这些配置文件并以应用编程语言对象的形式向应用程序提供配置信息。
-
模板引擎
模板引擎处理模板和一组变量以产生输出。模板是DSL的一个例子,可以使用Interpreter来解析和处理模板。
-
数学表达式计算器
数学表达式是我们日常都能接触到的,使用了一种特定领域语言语法书写语句或者叫表达式的实例。这些表达式在程序里可以使用解释器模式进行解析和解释。例如,计算器应用程序可以使用解释器来解析和评估用户输入的数学表达式。
-
自然语言处理
在更高级的情况下,解释器模式可用于解析和解释自然语言,不过这通常会涉及想机器学习这样的更复杂的技术。
解释器模式中的关键组件有:
- 表达式接口:表示抽象语法树的元素并定义解释表达式的方法。
- 具体表达式:实现表达式接口的结构,表示语言语法的各种规则或元素。
- 上下文对象:用于保存解释过程中所需的任何必要信息或状态。
- Parser 或 Builder:负责根据输入表达式构建抽象语法树的组件。
实现一个加减的运算器,我们对每种运算定义对应的Expression对象,在方法里实现具体的运算规则,避免所有的运算操作放到一个函数中,这体现了解释器模式的核心思想,将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。
定义数学运算这一领域语言里表示抽象语法树中元素的表达式接口:
type Expression interface {
Interpret() int
}
创建Expression接口的具体实现类,在我们的加减法运算中需要实现操作数、加法、减法对应的实现类。
type NumberExpression struct {
val int
}
// 解释--返回其整数值
func (n *NumberExpression) Interpret() int {
return n.val
}
// 加法运算
type AdditionExpression struct {
left, right Expression
}
// 解释--进行加法操作
func (n *AdditionExpression) Interpret() int {
return n.left.Interpret() + n.right.Interpret()
}
// 减法运算
type SubtractionExpression struct {
left, right Expression
}
// 解释--进行减法运算
func (n *SubtractionExpression) Interpret() int {
return n.left.Interpret() - n.right.Interpret()
}
创建一个表达式解析器,它会根据输入表达式构造抽象语法树,使用创建的抽象语法树和上下文解释表达式。
type Parser struct {
exp []string
index int
prev Expression
}
func (p *Parser) Parse(exp string) {
p.exp = strings.Split(exp, " ")
for {
if p.index >= len(p.exp) {
return
}
switch p.exp[p.index] {
case "+":
p.prev = p.newAdditionExpression()
case "-":
p.prev = p.newSubtractionExpression()
default:
p.prev = p.newNumberExpression()
}
}
}
func (p *Parser) newAdditionExpression() Expression {
p.index++
return &AdditionExpression{
left: p.prev,
right: p.newNumberExpression(),
}
}
func (p *Parser) newSubtractionExpression() Expression {
p.index++
return &SubtractionExpression{
left: p.prev,
right: p.newNumberExpression(),
}
}
func (p *Parser) newNumberExpression() Expression {
v, _ := strconv.Atoi(p.exp[p.index])
p.index++
return &NumberExpression{
val: v,
}
}
// 返回Expression实例
// 调用Interpret方法会从右向左递归计算出公式结果
func (p *Parser) Result() Expression {
return p.prev
}
使用 Parse 把客户端传递过来的加减法表达式解析成抽象语法树,然后运行解释器计算加减法表达式的结果。
func main() {
p := &Parser{}
p.Parse("1 + 3 + 3 + 3 - 3")
res := p.Result().Interpret()
expect := 7
if res != expect {
log.Fatalf("error: expect %d got %d", expect, res)
}
fmt.Printf("expect: %d, got: %d", expect, res)
}
解释器模式的优点是:
- 关注点分离:该模式将解释逻辑与数据表示分开。
- 可扩展性:可以通过添加新的表达式结构轻松地扩展模式。
- 可重用性:解释器模式可以在需要解析或解释特定领域语言的不同项目或上下文中重用。
缺点也很明显:很复杂,看懂代码就费时间,其次会很多次调用,在大型表达式的遍历语法树会很慢