一个菜鸟的设计模式之旅,文章可能会有不对的地方,恳请大佬指出错误。
编程旅途是漫长遥远的,在不同时刻有不同的感悟,本文会一直更新下去。
程序介绍

本程序实现解释器模式。程序可按需加载用户自定义的.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
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。
应用实例:编译器、运算表达式计算。
优点:
- 可扩展性比较好,灵活。
- 增加了新的解释表达式的方式。
- 易于实现简单文法。
缺点:
- 可利用场景比较少。
- 对于复杂的文法比较难维护。
- 解释器模式会引起类膨胀。
- 解释器模式采用递归调用方法。
解释器为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护。建议当文法复杂时,使用其他的技术如语法分析程序或编译器生成器来处理。
使用场景:
- 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
- 一些重复出现的问题可以用一种简单的语言来进行表达。
- 一个简单语法需要解释的场景。
扩展应用场景

参考资料
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |