这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天
本节实践课课讲了规则引擎的设计与实现
1.什么是规则引擎?
根据课程定义:规则引擎是一种嵌入在应用服务中的组件,可以将灵活多变的业务决策从服务代码中分离出来。通过使用预定义的语义模块来编写业务逻辑规则。在执行时接受数据输入、解释业务规则,并做出决策。规则引擎能大大提高系统的灵活性和扩展性。
实际当我们在对复杂的业务进行开发时,程序本身逻辑代码和业务代码经常出现互相嵌套等复杂模式,导致维护成本高,功能拓展性差,而规则引擎就是一个可以降低业务逻辑组件复杂性、降低应用程序维护成本、提高可拓展性的组件
举例而言,对于没有规则引擎的业务开发:
业务逻辑依靠业务人员与开发人员沟通,然后直接实行代码编写
而有了规则引擎后:
可以解决很多重复编码的问题,使业务决策与服务本身解耦
2.规则引擎原理
规则引擎内部符合编译原理对代码语句的分析规则,以课程展示的easyengine为例,包含组件:
词法分析器
识别代码语句中各种标识符token并进行错误判断等操作
代码片段:
type Scanner struct {
source []rune // 规则表达式字符串
position int // 遍历规则表达式过程中的位置
length int // 规则表达式字符串, 用于判断是否扫描结束
ch rune // position 位置对应的字符
}
// read returns the character at the pos of position and advancing
// the scanner. If the scanner is at Eof, read returns -1.
func (scanner *Scanner) read() rune {
var char rune
if !scanner.canRead() {
return eofRune
}
char = scanner.source[scanner.position]
scanner.ch = scanner.peek()
scanner.position += 1
return char
}
语法分析
检查语句中各种token组成的语法是否正确
代码片段
func (p *Parser) ParseSyntax() error {
// '(a + (b > c)' is illegal
err := p.checkBalance()
if err != nil {
return err
}
// 'param1 + 100 param2' is illegal
var lastTok token.Token
state, err := lastTok.Kind.GetLexerState()
for p.hasNext() {
tok := p.next()
if !state.CanTransitionTo(tok.Kind) {
return fmt.Errorf("cannot transition token types from %s [%v] to %s [%v]",
lastTok.Kind.String(), lastTok.Value, tok.Kind.String(), tok.Value)
}
state, err = tok.Kind.GetLexerState()
if err != nil {
return err
}
lastTok = tok
}
// 'a + b +' is illegal
if !state.IsEOF() {
return errors.New("unexpected end of expression")
}
p.Reset()
return nil
}
语法树构建
代码块以节点方式组建抽象语法树,以便后续以一定规则遍历语法树生成最终代码
node定义:
type Node struct {
symbol Symbol
value interface{}
tp TypeFlags
leftNode, rightNode *Node
// the operator that will be used to evaluate this node (such as adding [left] to [right] and return the result)
operator operator
// ensures that both left and right values are appropriate for this node. Returns an error if they aren't operable.
typeChecker typeChecker
}
// NewNodeWithPrefixFix The symbol `+` - can represent both unary and binary operators,
// and the priority of the operator is unique and needs to be adjusted on a case-by-case basis
// - In abstract syntax tree, if the left subtree of the node is empty and the right subtree is not empty,
// it can be judged to be a negative sign. The symbol needs to be corrected
// - If the right subtree of the right subtree is a symbol other than the prefix symbol [+、-],
// The node order needs to be corrected