一個菜鳥的設計模式之旅,使用 Golang 實現。本節實現解釋器模式。程式可按需載入用戶自定義的.work尾碼文件,將每行的命令解釋為具體行為。喵叫幾次、進程休眠幾秒、輸出範圍內隨機數、運行另外的work文件。 ...
一個菜鳥的設計模式之旅,文章可能會有不對的地方,懇請大佬指出錯誤。
編程旅途是漫長遙遠的,在不同時刻有不同的感悟,本文會一直更新下去。
程式介紹
本程式實現解釋器模式。程式可按需載入用戶自定義的.work尾碼文件,將每行的命令解釋為具體行為。喵叫幾次、進程休眠幾秒、輸出範圍內隨機數、運行另外的work文件。
Meow載入額外配置信息-----> 額外的配置信息
喵~喵~喵~喵~喵~
Sleep載入額外配置信息-----> 額外的配置信息
開始睡眠 3 s
Rand載入額外配置信息-----> 額外的配置信息
獲取 5~10 隨機數 -> 9
Sleep載入額外配置信息-----> 額外的配置信息
開始睡眠 5 s
Rand載入額外配置信息-----> 額外的配置信息
獲取 100~200 隨機數 -> 276
找不到該條指令的規則 瘋狂星期四
Call載入額外配置信息-----> 額外的配置信息
Meow載入額外配置信息-----> 額外的配置信息
喵~喵~
找不到該條指令的規則 rand
程式執行結束
Meow載入額外配置信息-----> 額外的配置信息
喵~
程式執行結束
程式代碼
data/duty.work
meow 5
sleep 3
random 5~10
sleep 5
random 100~200
瘋狂星期四 v我50
call ./data/test.work
meow 1
data/test.work
meow 2
rand 0~1
methods.go
package main
import (
"fmt"
"io"
"math/rand"
"strings"
"time"
)
func meow(count int) {
fmt.Println(strings.Repeat("喵~", count))
}
func sleep(count int) {
fmt.Printf("開始睡眠 %v s\n", count)
time.Sleep(time.Second * time.Duration(count))
}
func random(start, end int) {
fmt.Printf("獲取 %v~%v 隨機數 -> %v\n", start, end, rand.Intn(end)+start)
}
func doWork(path string) {
c := &context{comment: "額外的配置信息"}
c.Open(path)
for {
method, err := c.Read()
if err != io.EOF {
switch method {
case "meow":
var meow expression = &MeowExpression{}
meow.Interpret(c)
case "sleep":
var sleep expression = &SleepExpression{}
sleep.Interpret(c)
case "random":
var random expression = &RandomExpression{}
random.Interpret(c)
case "call":
var call expression = &CallExpression{}
call.Interpret(c)
default:
fmt.Println("找不到該條指令的規則", method)
}
} else {
break
}
}
fmt.Println("程式執行結束")
}
doWork()
函數用於創建上下文對象,並遍歷行,採用了簡單工廠模式。(略有違背單一職責原則,在這裡只做案例演示)
事實上每添加一種文法,就需要添加一個類,同時修改這個簡單工廠,對分支判斷進行修改。在這裡可以用反射改進,可以參考 “我的設計模式編程之旅 ①” 來動態生成實例對象,從而符合封閉-開放原則
。
interpreter.go
package main
import (
"bufio"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
)
type context struct {
file *os.File
scanner *bufio.Scanner
line string // ^ 當前行
comment string // ^ 額外配置信息
}
// 打開文件並轉換成 bufio.Scanner
func (c *context) Open(path string) {
file, err := os.Open(path)
if err != nil {
log.Fatal(err)
}
c.file = file
scanner := bufio.NewScanner(file)
c.scanner = scanner
}
// 關閉 bufio.Scanner
func (c *context) Close() {
c.file.Close()
c.file = nil
c.scanner = nil
}
// 獲取當前行並將游標移到下一行
func (c *context) Read() (string, error) {
if c.scanner == nil {
log.Fatal("no scanner")
}
if c.scanner.Scan() {
c.line = c.scanner.Text()
return strings.Split(c.line, " ")[0], nil
} else {
return "", io.EOF
}
}
type expression interface {
Interpret(c *context)
}
type MeowExpression struct{}
type SleepExpression struct{}
type RandomExpression struct{}
type CallExpression struct{}
func (e MeowExpression) Interpret(c *context) {
fmt.Println("Meow載入額外配置信息----->", c.comment)
params := strings.Split(c.line, " ")[1]
i, err := strconv.Atoi(params)
if err != nil {
log.Fatal(err)
}
meow(i)
}
func (e SleepExpression) Interpret(c *context) {
fmt.Println("Sleep載入額外配置信息----->", c.comment)
params := strings.Split(c.line, " ")[1]
i, err := strconv.Atoi(params)
if err != nil {
log.Fatal(err)
}
sleep(i)
}
func (e RandomExpression) Interpret(c *context) {
fmt.Println("Rand載入額外配置信息----->", c.comment)
params := strings.Split(c.line, " ")
params = strings.Split(params[1], "~")
start, err := strconv.Atoi(params[0])
if err != nil {
log.Fatal(err)
}
end, err := strconv.Atoi(params[1])
if err != nil {
log.Fatal(err)
}
random(start, end)
}
func (e CallExpression) Interpret(c *context) {
fmt.Println("Call載入額外配置信息----->", c.comment)
params := strings.Split(c.line, " ")
doWork(params[1])
}
main.go
package main
import (
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().Unix())
doWork("./data/duty.work")
}
Console
Meow載入額外配置信息-----> 額外的配置信息
喵~喵~喵~喵~喵~
Sleep載入額外配置信息-----> 額外的配置信息
開始睡眠 3 s
Rand載入額外配置信息-----> 額外的配置信息
獲取 5~10 隨機數 -> 9
Sleep載入額外配置信息-----> 額外的配置信息
開始睡眠 5 s
Rand載入額外配置信息-----> 額外的配置信息
獲取 100~200 隨機數 -> 276
找不到該條指令的規則 瘋狂星期四
Call載入額外配置信息-----> 額外的配置信息
Meow載入額外配置信息-----> 額外的配置信息
喵~喵~
找不到該條指令的規則 rand
程式執行結束
Meow載入額外配置信息-----> 額外的配置信息
喵~
程式執行結束
思考總結
什麼是解釋器模式
解釋器模式(Interpreter Pattern)提供了評估語言的語法或表達式的方式,它屬於行為型模式。這種模式實現了一個表達式介面,該介面解釋一個特定的上下文。這種模式被用在 SQL 解析、符號處理引擎等。解決的是一種類型的問題發生的頻率足夠高,可以將各個實例表述為簡單語言中的句子。
![image-20220909190141804](C:\Users\小能喵喵喵\Desktop\設計模式\筆記\設計模式之旅 ④ 解釋器模式.md.assets\image-20220909190141804.png)
解釋器模式:給定一個語言,定義它的文法(書寫規則結構)的一種表示,並定義一個解釋器,這個解釋器使用該標識來解釋語言中的句子。
意圖:給定一個語言,定義它的文法表示,並定義一個解釋器,這個解釋器使用該標識來解釋語言中的句子。
主要解決:對於一些固定文法構建一個解釋句子的解釋器。
何時使用:如果一種特定類型的問題發生的頻率足夠高,那麼可能就值得將該問題的各個實例表述為一個簡單語言中的句子。這樣就可以構建一個解釋器,該解釋器通過解釋這些句子來解決該問題。
如何解決:構建語法樹,定義終結符與非終結符。
關鍵代碼:構建環境類,包含解釋器之外的一些全局信息,一般是 HashMap。
應用實例:編譯器、運算表達式計算。
優點:
- 可擴展性比較好,靈活。
- 增加了新的解釋表達式的方式。
- 易於實現簡單文法。
缺點:
- 可利用場景比較少。
- 對於複雜的文法比較難維護。
- 解釋器模式會引起類膨脹。
- 解釋器模式採用遞歸調用方法。
解釋器為文法中的每一條規則至少定義了一個類,因此包含許多規則的文法可能難以管理和維護。建議當文法複雜時,使用其他的技術如語法分析程式或編譯器生成器來處理。
使用場景:
- 可以將一個需要解釋執行的語言中的句子表示為一個抽象語法樹。
- 一些重覆出現的問題可以用一種簡單的語言來進行表達。
- 一個簡單語法需要解釋的場景。
擴展應用場景
參考資料
- 《Go語言核心編程》李文塔
- 《Go語言高級編程》柴樹彬、曹春輝
- 《大話設計模式》程傑
- 單例模式 | 菜鳥教程