我的设计模式之旅 ④ 解释器模式.

打印 上一主题 下一主题

主题 934|帖子 934|积分 2802

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


本程序实现解释器模式。程序可按需加载用户自定义的.work后缀文件,将每行的命令解释为具体行为。喵叫几次、进程休眠几秒、输出范围内随机数、运行另外的work文件。
  1. Meow载入额外配置信息-----> 额外的配置信息
  2. 喵~喵~喵~喵~喵~
  3. Sleep载入额外配置信息-----> 额外的配置信息
  4. 开始睡眠 3 s
  5. Rand载入额外配置信息-----> 额外的配置信息
  6. 获取 5~10 随机数 -> 9
  7. Sleep载入额外配置信息-----> 额外的配置信息
  8. 开始睡眠 5 s
  9. Rand载入额外配置信息-----> 额外的配置信息
  10. 获取 100~200 随机数 -> 276
  11. 找不到该条指令的规则 疯狂星期四
  12. Call载入额外配置信息-----> 额外的配置信息
  13. Meow载入额外配置信息-----> 额外的配置信息
  14. 喵~喵~
  15. 找不到该条指令的规则 rand
  16. 程序执行结束
  17. Meow载入额外配置信息-----> 额外的配置信息
  18. 喵~
  19. 程序执行结束
复制代码
程序代码

data/duty.work
  1. meow 5
  2. sleep 3
  3. random 5~10
  4. sleep 5
  5. random 100~200
  6. 疯狂星期四 v我50
  7. call ./data/test.work
  8. meow 1
复制代码
data/test.work
  1. meow 2
  2. rand 0~1
复制代码
methods.go
  1. package main
  2. import (
  3.         "fmt"
  4.         "io"
  5.         "math/rand"
  6.         "strings"
  7.         "time"
  8. )
  9. func meow(count int) {
  10.         fmt.Println(strings.Repeat("喵~", count))
  11. }
  12. func sleep(count int) {
  13.         fmt.Printf("开始睡眠 %v s\n", count)
  14.         time.Sleep(time.Second * time.Duration(count))
  15. }
  16. func random(start, end int) {
  17.         fmt.Printf("获取 %v~%v 随机数 -> %v\n", start, end, rand.Intn(end)+start)
  18. }
  19. func doWork(path string) {
  20.         c := &context{comment: "额外的配置信息"}
  21.         c.Open(path)
  22.         for {
  23.                 method, err := c.Read()
  24.                 if err != io.EOF {
  25.                         switch method {
  26.                         case "meow":
  27.                                 var meow expression = &MeowExpression{}
  28.                                 meow.Interpret(c)
  29.                         case "sleep":
  30.                                 var sleep expression = &SleepExpression{}
  31.                                 sleep.Interpret(c)
  32.                         case "random":
  33.                                 var random expression = &RandomExpression{}
  34.                                 random.Interpret(c)
  35.                         case "call":
  36.                                 var call expression = &CallExpression{}
  37.                                 call.Interpret(c)
  38.                         default:
  39.                                 fmt.Println("找不到该条指令的规则", method)
  40.                         }
  41.                 } else {
  42.                         break
  43.                 }
  44.         }
  45.         fmt.Println("程序执行结束")
  46. }
复制代码
doWork()函数用于创建上下文对象,并遍历行,采用了简单工厂模式。(略有违背单一职责原则,在这里只做案例演示)
事实上每添加一种文法,就需要添加一个类,同时修改这个简单工厂,对分支判断进行修改。在这里可以用反射改进,可以参考 “我的设计模式编程之旅 ①” 来动态生成实例对象,从而符合封闭-开放原则。
interpreter.go
  1. package main
  2. import (
  3.         "bufio"
  4.         "fmt"
  5.         "io"
  6.         "log"
  7.         "os"
  8.         "strconv"
  9.         "strings"
  10. )
  11. type context struct {
  12.         file    *os.File
  13.         scanner *bufio.Scanner
  14.         line    string // ^ 当前行
  15.         comment string // ^ 额外配置信息
  16. }
  17. // 打开文件并转换成 bufio.Scanner
  18. func (c *context) Open(path string) {
  19.         file, err := os.Open(path)
  20.         if err != nil {
  21.                 log.Fatal(err)
  22.         }
  23.         c.file = file
  24.         scanner := bufio.NewScanner(file)
  25.         c.scanner = scanner
  26. }
  27. // 关闭 bufio.Scanner
  28. func (c *context) Close() {
  29.         c.file.Close()
  30.         c.file = nil
  31.         c.scanner = nil
  32. }
  33. // 获取当前行并将光标移到下一行
  34. func (c *context) Read() (string, error) {
  35.         if c.scanner == nil {
  36.                 log.Fatal("no scanner")
  37.         }
  38.         if c.scanner.Scan() {
  39.                 c.line = c.scanner.Text()
  40.                 return strings.Split(c.line, " ")[0], nil
  41.         } else {
  42.                 return "", io.EOF
  43.         }
  44. }
  45. type expression interface {
  46.         Interpret(c *context)
  47. }
  48. type MeowExpression struct{}
  49. type SleepExpression struct{}
  50. type RandomExpression struct{}
  51. type CallExpression struct{}
  52. func (e MeowExpression) Interpret(c *context) {
  53.         fmt.Println("Meow载入额外配置信息----->", c.comment)
  54.         params := strings.Split(c.line, " ")[1]
  55.         i, err := strconv.Atoi(params)
  56.         if err != nil {
  57.                 log.Fatal(err)
  58.         }
  59.         meow(i)
  60. }
  61. func (e SleepExpression) Interpret(c *context) {
  62.         fmt.Println("Sleep载入额外配置信息----->", c.comment)
  63.         params := strings.Split(c.line, " ")[1]
  64.         i, err := strconv.Atoi(params)
  65.         if err != nil {
  66.                 log.Fatal(err)
  67.         }
  68.         sleep(i)
  69. }
  70. func (e RandomExpression) Interpret(c *context) {
  71.         fmt.Println("Rand载入额外配置信息----->", c.comment)
  72.         params := strings.Split(c.line, " ")
  73.         params = strings.Split(params[1], "~")
  74.         start, err := strconv.Atoi(params[0])
  75.         if err != nil {
  76.                 log.Fatal(err)
  77.         }
  78.         end, err := strconv.Atoi(params[1])
  79.         if err != nil {
  80.                 log.Fatal(err)
  81.         }
  82.         random(start, end)
  83. }
  84. func (e CallExpression) Interpret(c *context) {
  85.         fmt.Println("Call载入额外配置信息----->", c.comment)
  86.         params := strings.Split(c.line, " ")
  87.         doWork(params[1])
  88. }
复制代码
main.go
  1. package main
  2. import (
  3.         "math/rand"
  4.         "time"
  5. )
  6. func main() {
  7.         rand.Seed(time.Now().Unix())
  8.         doWork("./data/duty.work")
  9. }
复制代码
Console
  1. Meow载入额外配置信息-----> 额外的配置信息
  2. 喵~喵~喵~喵~喵~
  3. Sleep载入额外配置信息-----> 额外的配置信息
  4. 开始睡眠 3 s
  5. Rand载入额外配置信息-----> 额外的配置信息
  6. 获取 5~10 随机数 -> 9
  7. Sleep载入额外配置信息-----> 额外的配置信息
  8. 开始睡眠 5 s
  9. Rand载入额外配置信息-----> 额外的配置信息
  10. 获取 100~200 随机数 -> 276
  11. 找不到该条指令的规则 疯狂星期四
  12. Call载入额外配置信息-----> 额外的配置信息
  13. Meow载入额外配置信息-----> 额外的配置信息
  14. 喵~喵~
  15. 找不到该条指令的规则 rand
  16. 程序执行结束
  17. Meow载入额外配置信息-----> 额外的配置信息
  18. 喵~
  19. 程序执行结束
复制代码
思考总结

什么是解释器模式

解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。解决的是一种类型的问题发生的频率足够高,可以将各个实例表述为简单语言中的句子。
![image-20220909190141804](C:\Users\小能喵喵喵\Desktop\设计模式\笔记\设计模式之旅 ④ 解释器模式.md.assets\image-20220909190141804.png)
解释器模式:给定一个语言,定义它的文法(书写规则结构)的一种表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
意图:给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
主要解决:对于一些固定文法构建一个解释句子的解释器。
何时使用:如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
如何解决:构建语法树,定义终结符与非终结符。
关键代码:构建环境类,包含解释器之外的一些全局信息,一般是 HashMap。
应用实例:编译器、运算表达式计算。
优点:

  • 可扩展性比较好,灵活。
  • 增加了新的解释表达式的方式。
  • 易于实现简单文法。
缺点:

  • 可利用场景比较少。
  • 对于复杂的文法比较难维护。
  • 解释器模式会引起类膨胀。
  • 解释器模式采用递归调用方法。
解释器为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护。建议当文法复杂时,使用其他的技术如语法分析程序或编译器生成器来处理。
使用场景:

  • 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
  • 一些重复出现的问题可以用一种简单的语言来进行表达。
  • 一个简单语法需要解释的场景。
扩展应用场景


参考资料


  • 《Go语言核心编程》李文塔
  • 《Go语言高级编程》柴树彬、曹春辉
  • 《大话设计模式》程杰
  • 单例模式 | 菜鸟教程

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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

麻花痒

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表