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