再次通过行为型模式视角回顾Go
形而上 Lv5

行为模式(Behavioral Pattern) 主要关注对象之间的职责分配和通信方式

行为型模式的核心是思想是通过定义对象之间的交互方式,实现系统功能,降低对象耦合度

行为型模式-责任链模式

避免请求的发送者和接收者之间耦合,将这些对象连成一条链,并沿着链传递请求,知道处理完为止

例如:Go的Gin框架里面的中间件
审批流

核心思想:

  1. 解耦请求发送者和接收者,请求发送者不需要知道具体哪个对象处理请求,只需要将请求发送到链上即可
  2. 动态组合处理逻辑,可以动态地组合处理者,灵活的调整处理顺序或者增加新的处理者
  3. 每个处理者只关系自己的职责
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package chain_of_responsibility

import (
"fmt"
"net/http"
)

type Context struct {
request *http.Request
w http.ResponseWriter
index int
handlers []HandlerFun
}

func (c *Context) Next() {
c.index++
if len(c.handlers) > c.index {
c.handlers[c.index](c)
}
}

func (c *Context) Abort() {
c.index = len(c.handlers)
}

type HandlerFun func(ctx *Context)

type Engine struct {
handlers []HandlerFun
}

func (e *Engine) Use(f HandlerFun) {
e.handlers = append(e.handlers, f)
}

func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
context := Context{
request: r,
w: w,
index: -1,
handlers: e.handlers,
}
context.Next()
}

func AuthMiddleware(ctx *Context) {
fmt.Println("认证中间件")
}

func LogMiddleware(ctx *Context) {
fmt.Println("日志中间件")
ctx.Next()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package chain_of_responsibility

import (
"fmt"
"net/http"
"testing"
)

func TestHandler(t *testing.T) {
r := &Engine{}
r.Use(LogMiddleware)
r.Use(AuthMiddleware)

fmt.Println("web server on : 8080")
http.ListenAndServe(":8080", r)
}

行为型模式-命令模式

将请求封装为一个对象,可以用不同的请求对客户进行参数化,并且支持请求排队,记录日志,撤销操作等功能

核心思想:

  1. 将请求封装为对象
  2. 解耦请求的接收者和发送者
  3. 支持扩展,不修改现有代码

例子:任务队列系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package command

import "fmt"

type Command interface {
Execute()
}

type PrintCommand struct {
Content string
}

func (p PrintCommand) Execute() {
fmt.Println("打印消息", p.Content)
}

type SendEmail struct {
To string
Content string
}

func (s SendEmail) Execute() {
fmt.Println("发送邮件", s.To, s.Content)
}

type TaskQueue struct {
Queue []Command
}

func NewTaskQueue() *TaskQueue {
return &TaskQueue{}
}

func (tq *TaskQueue) AddCommand(command Command) {
tq.Queue = append(tq.Queue, command)
}

func (tq *TaskQueue) Command() {
for _, command := range tq.Queue {
command.Execute()
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package command

import "testing"

func TestPrintCommand_Execute(t *testing.T) {
queue := NewTaskQueue()
queue.AddCommand(&PrintCommand{
Content: "你好",
})
queue.AddCommand(&SendEmail{
To: "xx@qq.com",
Content: "hello",
})
queue.Command()
}

行为型模式-解释器模式

定义语言的语法表示,提供解释器来解释语法,通常用于处理类似编程语言,查询语言,规则引擎等场景

核心思想:

  1. 定义语法规则
  2. 解释执行

案例:

  • 抽象语法树AST, 树形结构反映嵌套关系
  • 数据库查询引擎,mysql的查询解析和执行
  • 规则引擎,解析和执行规则
  • 模板引擎,前端开发
  • 数学表达式计算,解析计算数学
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package interpreter

import (
"fmt"
"strings"
"testing"
)

func TestFunc(t *testing.T) {
const tmpl = `Hello, {{.Name}}! You are {{.Age}} years old.`
template := ParseTemplate(tmpl)
fmt.Println(template.tree)
res := template.Interpreter(&Context{
Data: map[string]any{
".Name": "deepter",
".Age": 21,
},
})
fmt.Println(res)
}

type Context struct {
Data map[string]any
}

type Node interface {
Interpreter(ctx *Context) string
}

type TextNode struct {
Content string
}

func (t TextNode) Interpreter(ctx *Context) string {
return t.Content
}

type VarNode struct {
Key string
}

func (t VarNode) Interpreter(ctx *Context) string {
val, ok := ctx.Data[t.Key]
if !ok {
return ""
}
return fmt.Sprintf("%v", val)
}

type Template struct {
tree []Node
}

func ParseTemplate(tmpl string) *Template {
var template = new(Template)
var index = 0
for {
startIndex := strings.Index(tmpl[index:], "{{")
if startIndex == -1 {
template.tree = append(template.tree, &TextNode{
Content: tmpl[index:],
})
break
}
template.tree = append(template.tree, &TextNode{
Content: tmpl[index : index+startIndex],
})
endIndex := strings.Index(tmpl[index+startIndex:], "}}")
if endIndex == -1 {
break
}
key := strings.TrimSpace(tmpl[index+startIndex+2 : index+startIndex+endIndex])
template.tree = append(template.tree, &VarNode{
Key: key,
})
index = index + startIndex + endIndex + 2
}
return template
}

func (t *Template) Interpreter(ctx *Context) string {
var s string
for _, node := range t.tree {
s += node.Interpreter(ctx)
}
return s
}

行为型模式-迭代器模式

提供了一种方法,顺序访问聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

核心思想是:将遍历逻辑从聚合对象中分离出来,使聚合对象和遍历逻辑可以独立变化

实际应用场景:

  1. 集合类库,如Go的slice、map等集合类型,可以通过迭代器模式提供统一的遍历接口
  2. 数据库查询结果
  3. 文件系统遍历
  4. 树形结构遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package iterator

import (
"fmt"
"testing"
)

func TestIterator(t *testing.T) {
book := []*Book{
&Book{Title: "Go开发"},
{Title: "前端开发"},
{Title: "Java开发"},
}

iterator := NewBookIterator(book)
IteratorFunc(iterator)

}

type Book struct {
Title string
}

type BookIterator struct {
Books []*Book
position int
}

type Iterator interface {
HasNext() bool
Next() *Book
}

func (b *BookIterator) HasNext() bool {
if b.position >= len(b.Books) {
return false
}
return true
}

func (b *BookIterator) Next() *Book {
if !b.HasNext() {
return nil
}
book := b.Books[b.position]
b.position++
return book
}

func IteratorFunc(iterator Iterator) {
for iterator.HasNext() {
book := iterator.Next()
fmt.Println(book.Title)
}
}

func NewBookIterator(books []*Book) *BookIterator {
return &BookIterator{
Books: books,
position: 0,
}
}

行为型模式-中介者模式

引入一个中介者对象来封装一组对象之间的交互。

将对象之间的复杂交互集中到一个中介者对象中,减少对象间耦合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//实现一个用户发送给聊天室其他用户,其他用户收到消息,自己收不到
package mediator

import (
"fmt"
"testing"
)

type User struct {
Name string
mediator Mediator
}

type ChatRoom struct {
users []User
}

type Obj interface {
SendMsg(string)
RevMsg(string)
}

type Mediator interface {
SendMsg(msg string, user Obj)
}

func (u User) SendMsg(msg string) {
fmt.Printf("用户 %s 发了消息 %s\n", u.Name, msg)
u.mediator.SendMsg(msg, u)
}

func (u User) RevMsg(msg string) {
fmt.Printf("用户 %s 接收到消息 %s\n", u.Name, msg)
}

func (c *ChatRoom) SendMsg(msg string, user Obj) {
for _, u := range c.users {
if u == user {
continue
}
u.RevMsg(msg)
}
}

func (c *ChatRoom) Register(user User) {
c.users = append(c.users, user)
}

func TestSendMsg(t *testing.T) {
room := ChatRoom{}

u1 := User{Name: "ff", mediator: &room}
u2 := User{Name: "zs", mediator: &room}
u3 := User{Name: "ls", mediator: &room}

room.Register(u1)
room.Register(u2)
room.Register(u3)

u1.SendMsg("你好呀")
u2.SendMsg("你吃了吗")
u3.SendMsg("我吃了")

}

行为型模式-备忘录模式

允许在不破坏封装性的前提,捕获外部化一个内部状态,以便稍后可以将该对象恢复到之前的状态。

核心思想是 将对象状态保存到备忘录中,并在需要时从备忘录恢复状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package memento

import (
"fmt"
"testing"
)

type Node interface {
}

type TextNode struct {
state string
}

func (t *TextNode) SetState(state string) {
t.state = state
}

func (t *TextNode) GetState() string {
return t.state
}

func (t *TextNode) Save() Memento {
return &TextMemento{
state: t.state,
}
}

type Memento interface {
GetState() string
}

type TextMemento struct {
state string
}

func (t *TextMemento) GetState() string {
return t.state
}

type Manage struct {
states []Memento
}

func (m *Manage) Save(t Memento) {
m.states = append(m.states, t)
}

func (m *Manage) Back(index int) Memento {
return m.states[index]
}

func TestTextNode(t *testing.T) {
manage := Manage{}
text := TextNode{}
text.SetState("1")
manage.Save(text.Save())
text.SetState("2")
manage.Save(text.Save())
fmt.Println(text.GetState())
fmt.Println(manage.Back(0).GetState())
}

行为型模式-观察者模式

定义了对象之间一对多依赖关系,当一个对象(被观察者)的状态发生改变时,所有依赖它的对象(观察者)都会收到通知并自动更新。

有个天气站(被观察者), 天气数据更新了通知多个显示设备(观察者)更新显示内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package observer

import (
"fmt"
"testing"
)

type Observer interface {
RevMsg(wd int)
}

type Subject interface {
SendMsg(wd int)
Notify()
RegisterObserver(Observer)
}

type User struct {
Name string
}

func (u User) RevMsg(wd int) {
fmt.Printf("%s 现在温度 %d\n", u.Name, wd)
}

type WeatherState struct {
observers []Observer
wd int
}

func (w *WeatherState) SendMsg(wd int) {
w.wd = wd
w.Notify()
}

func (w *WeatherState) Notify() {
for _, observer := range w.observers {
observer.RevMsg(w.wd)
}
}

func (w *WeatherState) RegisterObserver(o Observer) {
w.observers = append(w.observers, o)
}

func TestObserver(t *testing.T) {
zhan := WeatherState{}
u1 := User{Name: "ff"}
u2 := User{Name: "zs"}
zhan.RegisterObserver(u1)
zhan.RegisterObserver(u2)

zhan.SendMsg(8)
zhan.SendMsg(0)
}

行为型模式-状态模式

在对象内部状态改变时,改变其行为, 状态模式的核心思想是将对象的状态封装成独立的类,并将其对象的行为委托给当前状态对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package state

import (
"fmt"
"testing"
)

type State interface {
Switch(context *Context)
}

type Context struct {
state State
}

func (c *Context) SetState(state State) {
c.state = state
}

func (c *Context) Switch() {
c.state.Switch(c)
}

type OnState struct {
}

func (OnState) Switch(context *Context) {
fmt.Println("开关关闭")
context.SetState(&OffState{})
}

type OffState struct {
}

func (OffState) Switch(context *Context) {
fmt.Println("开关打开")
context.SetState(&OnState{})
}

func TestState(t *testing.T) {
c := Context{
state: &OnState{},
}
c.Switch()
c.Switch()
c.Switch()
}

行为型模式-策略模式

定义了一系列算法,并将算法封装起来,是他们可以互相替换。

核心思想是将算法的使用和算法的实现分离,从而使算法可以独立于客户端而变化。

通过策略模式,可以运行时动态的选择算法,而不需要修改客户端代码

优点:

  1. 解耦,算法使用和算法分离
  2. 易于扩展,添加策略类
  3. 动态选择算法,不需要改客户端

比如支付系统,支持多种支付方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package strategy

import (
"fmt"
"testing"
)

type Pay interface {
Pay(money int64)
}

type AliPay struct {
}

type WeiPay struct {
}

func (AliPay) Pay(money int64) {
fmt.Printf("使用支付宝支付: %d 元\n", money)
}
func (WeiPay) Pay(money int64) {
fmt.Printf("使用微信支付: %d 元\n", money)
}

type PayStrategy struct {
pay Pay
}

func (p *PayStrategy) SetPay(pay Pay) {
p.pay = pay
}

func (p *PayStrategy) Pay(money int64) {
p.pay.Pay(money)
}

func TestPay(t *testing.T) {
aliPay := &AliPay{}
weiPay := &WeiPay{}
payStrategy := &PayStrategy{}
payStrategy.SetPay(aliPay)
payStrategy.Pay(12)
payStrategy.SetPay(weiPay)
payStrategy.Pay(10)
}

行为型模式-模板方法模式

定义算法骨架,将某些步骤延迟到子类实现。

核心思想是将算法通用部分放到父类中,将可变部分交给子类实现。

通过模板方法模式可以避免代码重复,确保算法结构不变

使用场景:

  1. 框架设计,web请求处理
  2. 工作流引擎
  3. 测试框架
  4. 游戏开发

假如有个饮料制作系统,支持制作咖啡,茶… 咖啡和茶制作过程有一些共同步骤,也有不同步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package template

import (
"fmt"
"testing"
)

type Template interface {
BoilWater()
Brew() //冲泡
AddSugar() //加糖
HasAddSugar() bool
}
type Coffee struct {
}

func (t *Coffee) BoilWater() {
fmt.Println("烧水")
}

func (t *Coffee) Brew() {
fmt.Println("冲泡")
}
func (t *Coffee) AddSugar() {
fmt.Println("加糖")
}
func (t *Coffee) HasAddSugar() bool {
return true
}

type Tea struct {
}

func (t *Tea) BoilWater() {
fmt.Println("烧水")
}
func (t *Tea) Brew() {
fmt.Println("冲泡")
}
func (t *Tea) AddSugar() {
fmt.Println("加糖")
}
func (t *Tea) HasAddSugar() bool {
return false
}

func MakeTemplate(tmp Template) {
tmp.BoilWater()
tmp.Brew()
if tmp.HasAddSugar() {
tmp.AddSugar()
}
}

func TestTemplate(t *testing.T) {
tea := Tea{}
coffee := Coffee{}
MakeTemplate(&tea)
MakeTemplate(&coffee)
}

行为型模式-访问者模式

将算法于对象结构分离。

核心思想:将算法从对象结构中分离出来是的可以在不修改对象结构情况下定义新得操作

通过访问者模式,可以将对象结构得元素和作用于这些元素得操作解耦,使得操作可以独立变化

有点:

  1. 解耦,操作和对象结构分离,操作可以独立变化
  2. 扩展性,新增新的访问者即可
  3. 符合开闭原则

假如我们有个文档对象模型,DOM ,包含多种元素,文本,图片,表格等, 我们需要对这些元素进行不通得操作(如导出pdf, 导出html等)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package visitor

import (
"fmt"
"testing"
)

type Text struct {
content string
}

type Element interface {
Accept(visitor Visitor)
}

type Image struct {
src string
}

func (t *Text) Accept(v Visitor) {
v.VisitText(t)
}

func (i *Image) Accept(v Visitor) {
v.VisitImage(i)
}

type Visitor interface {
VisitText(*Text)
VisitImage(*Image)
}

type PDFVisitor struct {
}

func (PDFVisitor) VisitText(text *Text) {
fmt.Println("pdf获取文本内容:", text.content)
}

func (PDFVisitor) VisitImage(image *Image) {
fmt.Println("pdf获取图片得src:", image.src)
}

type Document struct {
elements []Element
}

func (d *Document) AddElement(element Element) {
d.elements = append(d.elements, element)
}

func (d *Document) Accept(v Visitor) {
for _, element := range d.elements {
element.Accept(v)
}
}

func TestVisitor(t *testing.T) {
document := &Document{}
document.AddElement(&Text{content: "你好"})
document.AddElement(&Text{content: "吃饭了么"})
document.AddElement(&Text{content: "xxx"})
document.AddElement(&Image{src: "abc.png"})

pdf := &PDFVisitor{}
document.Accept(pdf)
}
  • 本文标题:再次通过行为型模式视角回顾Go
  • 本文作者:形而上
  • 创建时间:2022-07-08 00:00:00
  • 本文链接:https://deepter.gitee.io/2022_07_08_go_design_pattern/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!