我的設計模式之旅 ④ 解釋器模式.

来源:https://www.cnblogs.com/linxiaoxu/archive/2022/09/09/16673866.html
-Advertisement-
Play Games

一個菜鳥的設計模式之旅,使用 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。

應用實例:編譯器、運算表達式計算。

優點:

  • 可擴展性比較好,靈活。
  • 增加了新的解釋表達式的方式。
  • 易於實現簡單文法。

缺點:

  • 可利用場景比較少。
  • 對於複雜的文法比較難維護。
  • 解釋器模式會引起類膨脹。
  • 解釋器模式採用遞歸調用方法。

解釋器為文法中的每一條規則至少定義了一個類,因此包含許多規則的文法可能難以管理和維護。建議當文法複雜時,使用其他的技術如語法分析程式或編譯器生成器來處理。

使用場景:

  • 可以將一個需要解釋執行的語言中的句子表示為一個抽象語法樹。
  • 一些重覆出現的問題可以用一種簡單的語言來進行表達。
  • 一個簡單語法需要解釋的場景。

擴展應用場景

Interpreter Design Pattern

參考資料

  • 《Go語言核心編程》李文塔
  • 《Go語言高級編程》柴樹彬、曹春輝
  • 《大話設計模式》程傑
  • 單例模式 | 菜鳥教程

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JS使用parseInt()和正則截取字元串中數字 點擊打開視頻講解更加詳細 parseInt() 函數 定義和用法 parseInt() 函數可解析一個字元串,並返回一個整數。 當參數 radix 的值為 0,或沒有設置該參數時,parseInt() 會根據 string 來判斷數字的基數 當忽略 ...
  • Hello,大家好,我是你們的新朋友小烤鴨,我們的設計模式系列中斷了幾天,今天我們繼續給它續上,那麼我們下麵繼續來說一種結構型設計模式,那就是大名鼎鼎的“橋接模式”。 定義:橋接模式的官方定義是將抽象部分與它的實現部分分離,使得他們都可以獨立變化,是一種結構型對象設計模式;上面這個定義比較拗口,且晦 ...
  • 橋接(Bridge)是用於把抽象化與實現化解耦,使得二者可以獨立變化。這種類型的設計模式屬於結構型模式,它通過提供抽象化和實現化之間的橋接結構,來實現二者的解耦。 ...
  • 一個菜鳥的設計模式之旅,使用 Golang 實現。本節實現策略模式與簡單工廠。編程旅途是漫長遙遠的,在不同時刻有不同的感悟,本文會一直更新下去。 ...
  • 一個菜鳥的設計模式之旅,使用 Golang 實現。本節實現代理模式。小明很喜歡同班的雪怡,但是過於靦腆的他不敢當面說。打算通過手機(代理)跟雪怡表白! ...
  • 一個菜鳥的設計模式之旅,使用 Golang 實現。本節實現裝飾模式。小明和小王去吃沙縣小吃,各自喜歡不同的搭配,需要考慮每個人飲食喜好不同,隨時可能的變化。 ...
  • 2022-09-09 1、閉包的作用: 可以保存外部函數的變數 2、閉包的形成條件 (1)函數嵌套 (2)內部函數使用了外部函數的變數或者參數 (3)外部函數返回內部函數,這個使用了外部函數變數的內部函數稱為閉包 3、典例 1 # 函數嵌套 2 def func_out(): 3 # 外部函數 4 ...
  • 目錄 一.OpenGL 飽和度調節效果演示 1.IOS 飽和度演示效果 2.Windows OpenGL ES 飽和度演示效果 3.Windows OpenGL 飽和度演示效果 二.OpenGL 飽和度調節源碼下載 1.IOS Object-C 版本 2.Windows OpenGL ES 版本 3 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...