一个菜鸟的设计模式之旅,文章可能会有不对的地方,恳请大佬指出错误。
编程旅途是漫长遥远的,在不同时刻有不同的感悟,本文会一直更新下去。
程序介绍
本程序实现解释器模式。程序可按需加载用户自定义的.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 IExpression 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语言高级编程》柴树彬、曹春辉
- 《大话设计模式》程杰
- 单例模式 | 菜鸟教程
来源:https://www.cnblogs.com/linxiaoxu/p/16673866.html
本站部分图文来源于网络,如有侵权请联系删除。