一個菜鳥的設計模式之旅,文章可能會有不對的地方,懇請大佬指出錯誤,
編程旅途是漫長遙遠的,在不同時刻有不同的感悟,本文會一直更新下去,
程式介紹

本程式實作解釋器模式,程式可按需加載用戶自定義的.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 決議、符號處理引擎等,解決的是一種型別的問題發生的頻率足夠高,可以將各個實體表述為簡單語言中的句子,

解釋器模式:給定一個語言,定義它的文法(書寫規則結構)的一種表示,并定義一個解釋器,這個解釋器使用該標識來解釋語言中的句子,
意圖:給定一個語言,定義它的文法表示,并定義一個解釋器,這個解釋器使用該標識來解釋語言中的句子,
主要解決:對于一些固定文法構建一個解釋句子的解釋器,
何時使用:如果一種特定型別的問題發生的頻率足夠高,那么可能就值得將該問題的各個實體表述為一個簡單語言中的句子,這樣就可以構建一個解釋器,該解釋器通過解釋這些句子來解決該問題,
如何解決:構建語法樹,定義終結符與非終結符,
關鍵代碼:構建環境類,包含解釋器之外的一些全域資訊,一般是 HashMap,
應用實體:編譯器、運算運算式計算,
優點:
- 可擴展性比較好,靈活,
- 增加了新的解釋運算式的方式,
- 易于實作簡單文法,
缺點:
- 可利用場景比較少,
- 對于復雜的文法比較難維護,
- 解釋器模式會引起類膨脹,
- 解釋器模式采用遞回呼叫方法,
解釋器為文法中的每一條規則至少定義了一個類,因此包含許多規則的文法可能難以管理和維護,建議當文法復雜時,使用其他的技術如語法分析程式或編譯器生成器來處理,
使用場景:
- 可以將一個需要解釋執行的語言中的句子表示為一個抽象語法樹,
- 一些重復出現的問題可以用一種簡單的語言來進行表達,
- 一個簡單語法需要解釋的場景,
擴展應用場景

參考資料
- 《Go語言核心編程》李文塔
- 《Go語言高級編程》柴樹彬、曹春輝
- 《大話設計模式》程杰
- 單例模式 | 菜鳥教程
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/506014.html
標籤:其他
下一篇:OpenGL 飽和度調節
