原理
为新的"语言"定义语法规则,定义一个解释器用来解释语法
实现核心
将不同操作符的操作拆分到单独的操作符表达式类里面,避免大而全的解析类
最佳实践1
需求背景
假设我们定义了一个新的加减乘除计算“语言”,语法规则如下:
运算符只包含加、减、乘、除,并且没有优先级的概念;
表达式(也就是前面提到的“句子”)中,先书写数字,后书写运算符,空格隔开;
按照先后顺序,取出两个数字和一个运算符计算结果,结果重新放入数字的最头部位置,循环上述过程,直到只剩下一个数字,这个数字就是表达式最终的计算结果。
比如“ 8 3 2 4 - + * ”这样一个表达式,我们按照上面的语法规则来处理,取出数字“8 3”和“-”运算符,计算得到 5,于是表达式就变成了“ 5 2 4 + * ”。然后,我们再取出“ 5 2 ”和“ + ”运算符,计算得到 7,表达式就变成了“ 7 4 * ”。最后,我们取出“ 7 4”和“ * ”运算符,最终得到的结果就是 28。
实现版本1
type ExpressionInterpreter struct{}
func (i *ExpressionInterpreter) Interpreter(s string) int64 {
strList := strings.Split(s, " ")
numberQueue := queue.New()
for i := 0; i < (len(strList)+1)/2; i++ {
num, _ := strconv.ParseInt(strList[i], 10, 64)
numberQueue.PushBack(num)
}
for i := (len(strList) + 1) / 2; i < len(strList); i++ {
var result int64
num1 := numberQueue.PopFront().(int64)
num2 := numberQueue.PopFront().(int64)
sign := strList[i]
if sign == "+" {
result = num1 + num2
} else if sign == "-" {
result = num1 - num2
} else if sign == "*" {
result = num1 * num2
} else if sign == "/" {
result = num1 / num2
} else {
panic("illegal sign")
}
numberQueue.PushFront(result)
}
if numberQueue.Len() != 1 {
panic("expression illegal")
}
return numberQueue.PopFront().(int64)
}
这里每个运算符的逻辑比较简单,所以放在一个类中没有问题,但是如果每个运算符的逻辑都很复杂,就会导致这个类很大,可读性下降,所以我们可以将每个运算符的逻辑拆成单独的类
实现版本2
type Expression interface {
Interpreter() int64
}
type NumExpression struct {
num int64
}
func NewNumExpression(num int64) *NumExpression {
return &NumExpression{
num: num,
}
}
func (e *NumExpression) Interpreter() int64 {
return e.num
}
type AddExpression struct {
leftExp Expression
rightExp Expression
}
func NewAddExpression(leftExp Expression, rightExp Expression) *AddExpression {
return &AddExpression{
leftExp: leftExp,
rightExp: rightExp,
}
}
func (e *AddExpression) Interpreter() int64 {
return e.leftExp.Interpreter() + e.rightExp.Interpreter()
}
type SubtractExpression struct {
leftExp Expression
rightExp Expression
}
func NewSubtractExpression(leftExp Expression, rightExp Expression) *SubtractExpression {
return &SubtractExpression{
leftExp: leftExp,
rightExp: rightExp,
}
}
func (e *SubtractExpression) Interpreter() int64 {
return e.leftExp.Interpreter() - e.rightExp.Interpreter()
}
type MulExpression struct {
leftExp Expression
rightExp Expression
}
func NewMulExpression(leftExp Expression, rightExp Expression) *MulExpression {
return &MulExpression{
leftExp: leftExp,
rightExp: rightExp,
}
}
func (e *MulExpression) Interpreter() int64 {
return e.leftExp.Interpreter() * e.rightExp.Interpreter()
}
type DivisionExpression struct {
leftExp Expression
rightExp Expression
}
func NewDivisionExpression(leftExp Expression, rightExp Expression) *DivisionExpression {
return &DivisionExpression{
leftExp: leftExp,
rightExp: rightExp,
}
}
func (e *DivisionExpression) Interpreter() int64 {
return e.leftExp.Interpreter() / e.rightExp.Interpreter()
}
func (i *ExpressionInterpreter) InterpreterV2(s string) int64 {
strList := strings.Split(s, " ")
numberQueue := queue.New()
for i := 0; i < (len(strList)+1)/2; i++ {
num, _ := strconv.ParseInt(strList[i], 10, 64)
numberQueue.PushBack(num)
}
for i := (len(strList) + 1) / 2; i < len(strList); i++ {
var result int64
num1 := NewNumExpression(numberQueue.PopFront().(int64))
num2 := NewNumExpression(numberQueue.PopFront().(int64))
sign := strList[i]
var expression Expression
if sign == "+" {
expression = NewAddExpression(num1, num2)
} else if sign == "-" {
expression = NewSubtractExpression(num1, num2)
} else if sign == "*" {
expression = NewMulExpression(num1, num2)
} else if sign == "/" {
expression = NewDivisionExpression(num1, num2)
} else {
panic("illegal sign")
}
result = expression.Interpreter()
numberQueue.PushFront(result)
}
if numberQueue.Len() != 1 {
panic("expression illegal")
}
return numberQueue.PopFront().(int64)
}
最佳实践2
需求背景
在我们平时的项目开发中,监控系统非常重要,它可以时刻监控业务系统的运行情况,及时将异常报告给开发者。比如,如果每分钟接口出错数超过 100,监控系统就通过短信、微信、邮件等方式发送告警给开发者。
一般来讲,监控系统支持开发者自定义告警规则,比如我们可以用下面这样一个表达式,来表示一个告警规则,它表达的意思是:
每分钟 API 总出错数超过 100 或者每分钟 API 总调用数超过 10000 就触发告警。
api_error_per_minute > 100 || api_count_per_minute > 10000
我们假设自定义的告警规则只包含“||、&&、>、<、”这五个运算符,其中,“>、<、”运算符的优先级高于“||、&&”运算符,“&&”运算符优先级高于“||”。在表达式中,任意元素之间需要通过空格来分隔。除此之外,用户可以自定义要监控的 key,比如前面的 api_error_per_minute、api_count_per_minute。
代码实现
type AlterExpression interface {
Interpreter(stats map[string]int) bool
}
type GreaterExpression struct {
key string
value int
}
func NewGreaterExpression(key string, value int) *GreaterExpression {
return &GreaterExpression{
key: key,
value: value,
}
}
func (e *GreaterExpression) Interpreter(stats map[string]int) bool {
v, ok := stats[e.key]
if ok == false {
return false
}
return v > e.value
}
type LessExpression struct {
key string
value int
}
func NewLessExpression(key string, value int) *LessExpression {
return &LessExpression{
key: key,
value: value,
}
}
func (e *LessExpression) Interpreter(stats map[string]int) bool {
v, ok := stats[e.key]
if ok == false {
return false
}
return v < e.value
}
type EqualExpression struct {
key string
value int
}
func NewEqualExpression(key string, value int) *EqualExpression {
return &EqualExpression{
key: key,
value: value,
}
}
func (e *EqualExpression) Interpreter(stats map[string]int) bool {
v, ok := stats[e.key]
if ok == false {
return false
}
return e.value == v
}
type AndExpression struct {
expressionList []AlterExpression
}
func NewAndExpression(rule string) *AndExpression {
strList := strings.Split(rule, `&&`)
expList := make([]AlterExpression, 0, len(strList))
for i := range strList {
str := strings.Trim(strList[i], " ")
opStrList := strings.Split(str, " ")
sign := opStrList[1]
key := opStrList[0]
valueStr := opStrList[2]
value, _ := strconv.ParseInt(valueStr, 10, 64)
if sign == ">" {
expList = append(expList, NewGreaterExpression(key, int(value)))
} else if sign == "<" {
expList = append(expList, NewLessExpression(key, int(value)))
} else if sign == "" {
expList = append(expList, NewEqualExpression(key, int(value)))
} else {
panic("illegal op")
}
}
return &AndExpression{
expressionList: expList,
}
}
func (e *AndExpression) Interpreter(stats map[string]int) bool {
for i := range e.expressionList {
expression := e.expressionList[i]
if expression.Interpreter(stats) == false {
return false
}
}
return true
}
type OrExpression struct {
expressionList []AlterExpression
}
func NewOrExpression(rule string) *OrExpression {
strList := strings.Split(rule, `||`)
expList := make([]AlterExpression, 0, len(strList))
for i := range strList {
str := strings.Trim(strList[i], " ")
expList = append(expList, NewAndExpression(str))
}
return &OrExpression{
expressionList: expList,
}
}
func (e *OrExpression) Interpreter(stats map[string]int) bool {
for i := range e.expressionList {
expression := e.expressionList[i]
if expression.Interpreter(stats) {
return true
}
}
return false
}
type AlterRuleInterpreter struct {
expression AlterExpression
}
func NewAlterRuleInterpreter(ruleExpression string) *AlterRuleInterpreter {
return &AlterRuleInterpreter{
expression: NewOrExpression(ruleExpression),
}
}
func (i *AlterRuleInterpreter) Interpreter(stats map[string]int) bool {
return i.expression.Interpreter(stats)
}
func ApplicationTest() {
rule := "key1 > 100 && key2 < 30 || key3 < 100 || key4 == 88"
alterRuleInterpreter := NewAlterRuleInterpreter(rule)
stats := map[string]int{
"key1": 101,
"key2": 18,
"key3": 121,
"key4": 88,
}
alter := alterRuleInterpreter.Interpreter(stats)
if alter {
println("报警!!!")
}
}