Go设计模式22-解释器模式

注:本文已发布超过一年,请注意您所使用工具的相关版本是否适用

笔记

代码实现

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// 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
}

单元测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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))
})
}
}

关注我获取更新

wechat
知乎
github

猜你喜欢


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议,转载请注明出处,禁止全文转载