解释器模式可以描述如何构建一个简单的“语言”解释器。这个模式只在一些特定的领域才有可能用到,如编译器、规则引擎、正则表达式等。好在解释器模式比较简单,大家可以了解一下。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
UML:
通过定义可以看出,“语言”必然存在规则,而且规则可能相对复杂。对于每一条规则,可以设置对应的类来翻译。
模式定义与UML完全对应,解释器Expression用于翻译Context,由于Context包含内容类型不同,对这些不同类型的内容创建不同解释器进行解释。
举个例子,假设定义一个新的加减乘除语言,对于 8 + 2 - 5这种情况,新的语言为 8 2 5 + -。在这个例子中,Context就是8 2 5 + -,新语言包含数字和运算符,我们就可以创建数字解释器和运算符解释器,对数字和运算符进行解释。
为什么要用解释器模式呢?
主要是为了将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。
如果打算用解释器模式,一般的做法是,将语法规则拆分成一些小的独立的单元,然后对每个单元进行解析,最终合并为对整个语法规则的解析。
解释器模式一般用于编译器、规则引擎、正则表达式,这些功能实现起来都比较麻烦,就不实现它们了。
在实际生活中,我对于中文、英文、编程语言都有一些了解,所以能够解释、理解它们。但对于音乐、舞蹈,根本不知道怎么欣赏。
所以看歌舞表演的时候,特别想有一款翻译器,能对音乐、舞蹈进行翻译,让我了解创作者想表达什么。而且查看解释器源码我也能知道音乐、舞蹈的一般规则,更便于今后的学习。
现在让我们开始翻译歌舞剧!
package main
import "fmt"
/**
* @Description: 内容信息
*/
type Context struct {
action string
content string
}
/**
* @Description: 翻译接口
*/
type Interpreter interface {
Interpret(c Context)
}
/**
* @Description: 翻译音乐
*/
type MusicInterpreter struct {
}
/**
* @Description: 翻译音乐内容
* @receiver m
* @param c
*/
func (m MusicInterpreter) Interpret(c Context) {
fmt.Println(c.action + " 中 " + c.content + " 的意思是感情高昂")
}
/**
* @Description: 翻译舞蹈
*/
type DanceInterpreter struct {
}
/**
* @Description: 翻译舞蹈内容
* @receiver d
* @param c
*/
func (d DanceInterpreter) Interpret(c Context) {
fmt.Println(c.action + " 中 " + c.content + " 的意思是悲凉")
}
func main() {
cList := []Context{
{action: "music", content: "高音"},
{action: "music", content: "低音"},
{action: "dance", content: "跳跃"},
{action: "dance", content: "挥手"},
}
//对歌舞剧内容进行翻译
for _, c := range cList {
if c.action == "music" {
MusicInterpreter{}.Interpret(c)
} else if c.action == "dance" {
DanceInterpreter{}.Interpret(c)
}
}
}
➜ myproject go run main.go
music 中 高音 的意思是感情高昂
music 中 低音 的意思是感情高昂
dance 中 跳跃 的意思是悲凉
dance 中 挥手 的意思是悲凉
// Package interpreter 解释器模式
// 采用原课程的示例, 并且做了一下简化
// 假设我们现在有一个监控系统
// 现在需要实现一个告警模块,可以根据输入的告警规则来决定是否触发告警
// 告警规则支持 &&、>、< 3种运算符
// 其中 >、< 优先级比 && 更高
package interpreter
import (
"fmt"
"regexp"
"strconv"
"strings"
)
// AlertRule 告警规则
type AlertRule struct {
expression IExpression
}
// NewAlertRule NewAlertRule
func NewAlertRule(rule string) (*AlertRule, error) {
exp, err := NewAndExpression(rule)
return &AlertRule{expression: exp}, err
}
// Interpret 判断告警是否触发
func (r AlertRule) Interpret(stats map[string]float64) bool {
return r.expression.Interpret(stats)
}
// IExpression 表达式接口
type IExpression interface {
Interpret(stats map[string]float64) bool
}
// GreaterExpression >
type GreaterExpression struct {
key string
value float64
}
// Interpret Interpret
func (g GreaterExpression) Interpret(stats map[string]float64) bool {
v, ok := stats[g.key]
if !ok {
return false
}
return v > g.value
}
// NewGreaterExpression NewGreaterExpression
func NewGreaterExpression(exp string) (*GreaterExpression, error) {
data := regexp.MustCompile(`\s+`).Split(strings.TrimSpace(exp), -1)
if len(data) != 3 || data[1] != ">" {
return nil, fmt.Errorf("exp is invalid: %s", exp)
}
val, err := strconv.ParseFloat(data[2], 10)
if err != nil {
return nil, fmt.Errorf("exp is invalid: %s", exp)
}
return &GreaterExpression{
key: data[0],
value: val,
}, nil
}
// LessExpression <
type LessExpression struct {
key string
value float64
}
// Interpret Interpret
func (g LessExpression) Interpret(stats map[string]float64) bool {
v, ok := stats[g.key]
if !ok {
return false
}
return v < g.value
}
// NewLessExpression NewLessExpression
func NewLessExpression(exp string) (*LessExpression, error) {
data := regexp.MustCompile(`\s+`).Split(strings.TrimSpace(exp), -1)
if len(data) != 3 || data[1] != "<" {
return nil, fmt.Errorf("exp is invalid: %s", exp)
}
val, err := strconv.ParseFloat(data[2], 10)
if err != nil {
return nil, fmt.Errorf("exp is invalid: %s", exp)
}
return &LessExpression{
key: data[0],
value: val,
}, nil
}
// AndExpression &&
type AndExpression struct {
expressions []IExpression
}
// Interpret Interpret
func (e AndExpression) Interpret(stats map[string]float64) bool {
for _, expression := range e.expressions {
if !expression.Interpret(stats) {
return false
}
}
return true
}
// NewAndExpression NewAndExpression
func NewAndExpression(exp string) (*AndExpression, error) {
exps := strings.Split(exp, "&&")
expressions := make([]IExpression, len(exps))
for i, e := range exps {
var expression IExpression
var err error
switch {
case strings.Contains(e, ">"):
expression, err = NewGreaterExpression(e)
case strings.Contains(e, "<"):
expression, err = NewLessExpression(e)
default:
err = fmt.Errorf("exp is invalid: %s", exp)
}
if err != nil {
return nil, err
}
expressions[i] = expression
}
return &AndExpression{expressions: expressions}, nil
}
package interpreter
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAlertRule_Interpret(t *testing.T) {
stats := map[string]float64{
"a": 1,
"b": 2,
"c": 3,
}
tests := []struct {
name string
stats map[string]float64
rule string
want bool
}{
{
name: "case1",
stats: stats,
rule: "a > 1 && b > 10 && c < 5",
want: false,
},
{
name: "case2",
stats: stats,
rule: "a < 2 && b > 10 && c < 5",
want: false,
},
{
name: "case3",
stats: stats,
rule: "a < 5 && b > 1 && c < 10",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r, err := NewAlertRule(tt.rule)
require.NoError(t, err)
assert.Equal(t, tt.want, r.Interpret(tt.stats))
})
}
}
解释器模式主要是分而治之,将翻译功能分开管理,方便维护。翻译器模式理解难度和使用难度不大,主要是使用场景比较受限。