我的设计模式之旅 ④ 解释器模式.
作者:互联网
一个菜鸟的设计模式之旅,文章可能会有不对的地方,恳请大佬指出错误。
编程旅途是漫长遥远的,在不同时刻有不同的感悟,本文会一直更新下去。
程序介绍
本程序实现解释器模式。程序可按需加载用户自定义的.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语言高级编程》柴树彬、曹春辉
- 《大话设计模式》程杰
- 单例模式 | 菜鸟教程
标签:解释器,额外,err,之旅,配置,信息,载入,-----,设计模式 来源: https://www.cnblogs.com/linxiaoxu/p/16673866.html