Go設計模式--解釋器模式
大家好,這里是每周都在陪你一起進步的網(wǎng)管~!今天繼續(xù)學習設計模式—解釋器模式
解釋器模式是一種行為設計模式,可以用來在程序里創(chuàng)建針對一個特點領域語言的解釋器,用于處理解釋領域語言中的語句。換句話說,該模式定義了領域語言的抽象語法樹以及用示來解釋語法樹的解釋器。
模式使用場景
解釋器模式,用于解決需要解釋語言中的句子或表達式的問題。以下是一些可以在 程序中使用解釋器模式的真實場景:
- 處理配置文件
許多應用程序使用配置文件來指定應用程序的行為方式。這些配置文件可以用 YAML 或 JSON 等 DSL 編寫。解釋器可用于解析這些配置文件并以應用編程語言對象的形式向應用程序提供配置信息。
- 模板引擎
- 模板引擎處理模板和一組變量以產(chǎn)生輸出。模板是DSL的一個例子,可以使用Interpreter來解析和處理模板。
- 數(shù)學表達式計算器
- 數(shù)學表達式是我們?nèi)粘6寄芙佑|到的,使用了一種特定領域語言語法書寫語句或者叫表達式的實例
- 這些表達式在程序里可以使用解釋器模式進行解析和解釋。例如,計算器應用程序可以使用解釋器來解析和評估用戶輸入的數(shù)學表達式。
- 自然語言處理
- 在更高級的情況下,解釋器模式可用于解析和解釋自然語言,不過這通常會涉及想機器學習這樣的更復雜的技術。
雖然解釋器模式可以用來解決這些問題,但它并不總是最好的解決方案。對于復雜的語言,使用特定的解析庫或工具或其他設計模式可能更有效。
下面我們先來學習一下解釋器模式的結構組成,然后再嘗試用代碼自己實現(xiàn)一個解釋器。
模式構成
解釋器模式中的關鍵組件有:
- 表達式接口:表示抽象語法樹的元素并定義解釋表達式的方法。
- 具體表達式:實現(xiàn)表達式接口的結構,表示語言語法的各種規(guī)則或元素。
- 上下文對象:用于保存解釋過程中所需的任何必要信息或狀態(tài)。
- Parser 或 Builder:負責根據(jù)輸入表達式構建抽象語法樹的組件。
下面是解釋器模式構成的UML類圖:
看完解釋器模式的結構組成后,我們接下來嘗試應用解釋器模式,用代碼實現(xiàn)一個加法運算的解釋器。
實現(xiàn)解釋器模式
看了上面解釋器的結構組成后我們結下來通過代碼一步步實現(xiàn)其核心組件來演示怎么用代碼實現(xiàn)解釋器模式。
以下是如何在 Go 中實現(xiàn)解釋器模式的步驟。
- 定義表示抽象語法樹中元素的表達式接口。
- 創(chuàng)建實現(xiàn) Expression 接口的具體表達式結構,例如 TerminalExpression 和 NonTerminalExpression。
- 定義一個上下文結構來保存解釋過程中可能需要的任何必要數(shù)據(jù)或狀態(tài)(這一步可選)。
- 創(chuàng)建解析器或構建器以根據(jù)輸入表達式構造抽象語法樹。 使用創(chuàng)建的抽象語法樹和上下文解釋表達式。
這里簡單實現(xiàn)一個加減的運算器,我們對每種運算定義對應的Expression對象,在方法里實現(xiàn)具體的運算規(guī)則,避免所有的運算操作放到一個函數(shù)中,這體現(xiàn)了解釋器模式的核心思想,將語法解析的工作拆分到各個小類中,以此來避免大而全的解析類。
我們先按照上面的步驟一,定義數(shù)學運算這一領域語言里表示抽象語法樹中元素的表達式接口:
type Expression interface {
Interpret() int
}
接下來創(chuàng)建Expression接口的具體實現(xiàn)類,在我們的加減法運算中需要實現(xiàn)操作數(shù)、加法、減法對應的實現(xiàn)類。
"本文使用的完整可運行源碼
去公眾號「網(wǎng)管叨bi叨」發(fā)送【設計模式】即可領取"
type NumberExpression struct {
val int
}
// 解釋--返回其整數(shù)值
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()
}
最后我們創(chuàng)建一個表達式解析器,它會根據(jù)輸入表達式構造抽象語法樹,使用創(chuàng)建的抽象語法樹和上下文解釋表達式。
"本文使用的完整可運行源碼
去公眾號「網(wǎng)管叨bi叨」發(fā)送【設計模式】即可領取"
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 把客戶端傳遞過來的加減法表達式解析成抽象語法樹,然后運行解釋器計算加減法表達式的結果。
"本文使用的完整可運行源碼
去公眾號「網(wǎng)管叨bi叨」發(fā)送【設計模式】即可領取"
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)
}
總結
在程序中使用解釋器模式的目標是: 定義特定于領域的語言及其語法,使用 AST(抽象語法樹)表示語言中的表達式或句子,好讓程序能夠根據(jù)一組規(guī)則或操作解釋或評估表達式
最后我們再來列舉一下解釋器模式的優(yōu)缺點。 使用解釋器模式的優(yōu)點是:
- 關注點分離:該模式將解釋邏輯與數(shù)據(jù)表示分開。
- 可擴展性:可以通過添加新的表達式結構輕松地擴展模式。
- 可重用性:解釋器模式可以在需要解析或解釋特定領域語言的不同項目或上下文中重用。
使用解釋器模式的缺點是:
- 復雜性:隨著語法規(guī)則數(shù)量的增加,模式會變得復雜。
- 性能:對于大型表達式,抽象語法樹的遞歸遍歷可能很慢。