行为模式

责任链模式

责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。比如 kratos,gin等开源库的中间件实现。

代码实现

 1package main
 2
 3import (
 4	"context"
 5	"fmt"
 6)
 7
 8type Handler func(ctx context.Context, req interface{}) (resp interface{}, err error)
 9
10type Middleware func(next Handler) Handler
11
12func Chain(middlewares ...Middleware) Middleware {
13	return func(next Handler) Handler {
14		for i := len(middlewares) - 1; i >= 0; i-- {
15			next = middlewares[i](next)
16		}
17		return next
18	}
19}
20
21func main() {
22	c := Chain(func(next Handler) Handler {
23		return func(ctx context.Context, req interface{}) (resp interface{}, err error) {
24			fmt.Println("handler 1 before")
25			resp, err = next(ctx, req)
26			fmt.Println("handler 1 after")
27			return resp, err
28		}
29	}, func(next Handler) Handler {
30		return func(ctx context.Context, req interface{}) (resp interface{}, err error) {
31			fmt.Println("handler 2 before")
32			resp, err = next(ctx, req)
33			fmt.Println("handler 2 after")
34			return resp, err
35		}
36	})
37	resp, err := c(func(ctx context.Context, req interface{}) (resp interface{}, err error) {
38		fmt.Println("handler req:", req)
39		return req, nil
40	})(context.Background(), "hello")
41	fmt.Println(resp, err)
42}
43
44/*
45handler 1 before
46handler 2 before
47handler req: hello
48handler 2 after
49handler 1 after
50hello <nil>
51*/

观察者模式

观察者模式用于触发联动。一个对象的改变会触发其它观察者的相关动作,而此对象无需关心连动对象的具体实现。

代码实现

 1package main
 2
 3import "fmt"
 4
 5type subject interface {
 6	register(Observer observer)
 7	deregister(Observer observer)
 8	notifyAll()
 9}
10
11type observer interface {
12	update(string)
13	getID() string
14}
15
16type item struct {
17	observerList []observer
18	name         string
19	inStock      bool
20}
21
22func newItem(name string) *item {
23	return &item{
24		name: name,
25	}
26}
27func (i *item) updateAvailability() {
28	fmt.Printf("Item %s is now in stock\n", i.name)
29	i.inStock = true
30	i.notifyAll()
31}
32func (i *item) register(o observer) {
33	i.observerList = append(i.observerList, o)
34}
35
36func (i *item) deregister(o observer) {
37	i.observerList = removeFromslice(i.observerList, o)
38}
39
40func (i *item) notifyAll() {
41	for _, observer := range i.observerList {
42		observer.update(i.name)
43	}
44}
45
46func removeFromslice(observerList []observer, observerToRemove observer) []observer {
47	observerListLength := len(observerList)
48	for i, observer := range observerList {
49		if observerToRemove.getID() == observer.getID() {
50			observerList[observerListLength-1], observerList[i] = observerList[i], observerList[observerListLength-1]
51			return observerList[:observerListLength-1]
52		}
53	}
54	return observerList
55}
56
57type customer struct {
58	id string
59}
60
61func (c *customer) update(itemName string) {
62	fmt.Printf("Sending email to customer %s for item %s\n", c.id, itemName)
63}
64
65func (c *customer) getID() string {
66	return c.id
67}
68
69func main() {
70
71	shirtItem := newItem("Nike Shirt")
72
73	observerFirst := &customer{id: "abc@gmail.com"}
74	observerSecond := &customer{id: "xyz@gmail.com"}
75
76	shirtItem.register(observerFirst)
77	shirtItem.register(observerSecond)
78
79	shirtItem.updateAvailability()
80}
81
82/*
83Item Nike Shirt is now in stock
84Sending email to customer abc@gmail.com for item Nike Shirt
85Sending email to customer xyz@gmail.com for item Nike Shirt
86*/

模板方法模式

模版方法模式使用继承机制,把通用步骤和通用方法放到父类中,把具体实现延迟到子类中实现。使得实现符合开闭原则。

如实例代码中通用步骤在父类中实现(准备下载保存收尾)下载和保存的具体实现留到子类中,并且提供 保存方法的默认实现。

因为Golang不提供继承机制,需要使用匿名组合模拟实现继承。

此处需要注意:因为父类需要调用子类方法,所以子类需要匿名组合父类的同时,父类需要持有子类的引用。

代码实现

 1package main
 2
 3import "fmt"
 4
 5type Downloader interface {
 6	Download(uri string)
 7}
 8
 9type template struct {
10	implement
11	uri string
12}
13
14type implement interface {
15	download()
16	save()
17}
18
19func newTemplate(impl implement) *template {
20	return &template{
21		implement: impl,
22	}
23}
24
25func (t *template) Download(uri string) {
26	t.uri = uri
27	fmt.Print("prepare downloading\n")
28	t.implement.download()
29	t.implement.save()
30	fmt.Print("finish downloading\n")
31}
32
33func (t *template) save() {
34	fmt.Print("default save\n")
35}
36
37type HTTPDownloader struct {
38	*template
39}
40
41func NewHTTPDownloader() Downloader {
42	downloader := &HTTPDownloader{}
43	template := newTemplate(downloader)
44	downloader.template = template
45	return downloader
46}
47
48func (d *HTTPDownloader) download() {
49	fmt.Printf("download %s via http\n", d.uri)
50}
51
52func (*HTTPDownloader) save() {
53	fmt.Printf("http save\n")
54}
55
56type FTPDownloader struct {
57	*template
58}
59
60func NewFTPDownloader() Downloader {
61	downloader := &FTPDownloader{}
62	template := newTemplate(downloader)
63	downloader.template = template
64	return downloader
65}
66
67func (d *FTPDownloader) download() {
68	fmt.Printf("download %s via ftp\n", d.uri)
69}
70
71func main() {
72	downloader := NewHTTPDownloader()
73	downloader.Download("http://example.com/abc.zip")
74
75	downloader = NewFTPDownloader()
76	downloader.Download("ftp://example.com/abc.zip")
77}
78
79/*
80prepare downloading
81download http://example.com/abc.zip via http
82http save
83finish downloading
84prepare downloading
85download ftp://example.com/abc.zip via ftp
86default save
87finish downloading
88*/

命令模式

命令模式是一种行为设计模式, 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中, 且能实现可撤销操作。

命令模式本质是把某个对象的方法调用封装到对象中,方便传递、存储、调用。

示例中把主板单中的启动(start)方法和重启(reboot)方法封装为命令对象,再传递到主机(box)对象中。于两个按钮进行绑定:

  • 第一个机箱(box1)设置按钮1(button1) 为开机按钮2(button2)为重启。
  • 第二个机箱(box1)设置按钮2(button2) 为开机按钮1(button1)为重启。

从而得到配置灵活性。

除了配置灵活外,使用命令模式还可以用作:

  • 批处理
  • 任务队列
  • undo, redo

等把具体命令封装到对象中使用的场合

代码实现

 1package command
 2
 3import "fmt"
 4
 5type Command interface {
 6	Execute()
 7}
 8
 9type StartCommand struct {
10	mb *MotherBoard
11}
12
13func NewStartCommand(mb *MotherBoard) *StartCommand {
14	return &StartCommand{
15		mb: mb,
16	}
17}
18
19func (c *StartCommand) Execute() {
20	c.mb.Start()
21}
22
23type RebootCommand struct {
24	mb *MotherBoard
25}
26
27func NewRebootCommand(mb *MotherBoard) *RebootCommand {
28	return &RebootCommand{
29		mb: mb,
30	}
31}
32
33func (c *RebootCommand) Execute() {
34	c.mb.Reboot()
35}
36
37type MotherBoard struct{}
38
39func (*MotherBoard) Start() {
40	fmt.Print("system starting\n")
41}
42
43func (*MotherBoard) Reboot() {
44	fmt.Print("system rebooting\n")
45}
46
47type Box struct {
48	button1 Command
49	button2 Command
50}
51
52func NewBox(button1, button2 Command) *Box {
53	return &Box{
54		button1: button1,
55		button2: button2,
56	}
57}
58
59func (b *Box) PressButton1() {
60	b.button1.Execute()
61}
62
63func (b *Box) PressButton2() {
64	b.button2.Execute()
65}

策略模式

它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。

代码实现

 1package main
 2
 3import "fmt"
 4
 5type Payment struct {
 6	context  *PaymentContext
 7	strategy PaymentStrategy
 8}
 9
10type PaymentContext struct {
11	Name, CardID string
12	Money        int
13}
14
15func NewPayment(name, cardid string, money int, strategy PaymentStrategy) *Payment {
16	return &Payment{
17		context: &PaymentContext{
18			Name:   name,
19			CardID: cardid,
20			Money:  money,
21		},
22		strategy: strategy,
23	}
24}
25
26func (p *Payment) Pay() {
27	p.strategy.Pay(p.context)
28}
29
30type PaymentStrategy interface {
31	Pay(*PaymentContext)
32}
33
34type Cash struct{}
35
36func (*Cash) Pay(ctx *PaymentContext) {
37	fmt.Printf("Pay $%d to %s by cash\n", ctx.Money, ctx.Name)
38}
39
40type Bank struct{}
41
42func (*Bank) Pay(ctx *PaymentContext) {
43	fmt.Printf("Pay $%d to %s by bank account %s\n", ctx.Money, ctx.Name, ctx.CardID)
44}
45
46func main() {
47	payment := NewPayment("Ada", "", 123, &Cash{})
48	payment.Pay()
49
50	payment = NewPayment("Bob", "0002", 888, &Bank{})
51	payment.Pay()
52}
53
54/*
55Pay $123 to Ada by cash
56Pay $888 to Bob by bank account 0002
57*/

状态模式

让你能在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。

代码实现

  1package main
  2
  3import (
  4	"fmt"
  5)
  6
  7// Machine 状态机
  8type Machine struct {
  9	state IState
 10}
 11
 12// SetState 更新状态
 13func (m *Machine) SetState(state IState) {
 14	m.state = state
 15}
 16
 17// GetStateName 获取当前状态
 18func (m *Machine) GetStateName() string {
 19	return m.state.GetName()
 20}
 21
 22func (m *Machine) Approval() {
 23	m.state.Approval(m)
 24}
 25
 26func (m *Machine) Reject() {
 27	m.state.Reject(m)
 28}
 29
 30// IState 状态
 31type IState interface {
 32	// 审批通过
 33	Approval(m *Machine)
 34	// 驳回
 35	Reject(m *Machine)
 36	// 获取当前状态名称
 37	GetName() string
 38}
 39
 40// leaderApproveState 直属领导审批
 41type leaderApproveState struct{}
 42
 43// Approval 获取状态名字
 44func (leaderApproveState) Approval(m *Machine) {
 45	fmt.Println("leader 审批成功")
 46	m.SetState(GetFinanceApproveState())
 47}
 48
 49// GetName 获取状态名字
 50func (leaderApproveState) GetName() string {
 51	return "LeaderApproveState"
 52}
 53
 54// Reject 获取状态名字
 55func (leaderApproveState) Reject(m *Machine) {}
 56
 57func GetLeaderApproveState() IState {
 58	return &leaderApproveState{}
 59}
 60
 61// financeApproveState 财务审批
 62type financeApproveState struct{}
 63
 64// Approval 审批通过
 65func (f financeApproveState) Approval(m *Machine) {
 66	fmt.Println("财务审批成功")
 67	fmt.Println("出发打款操作")
 68}
 69
 70// 拒绝
 71func (f financeApproveState) Reject(m *Machine) {
 72	m.SetState(GetLeaderApproveState())
 73}
 74
 75// GetName 获取名字
 76func (f financeApproveState) GetName() string {
 77	return "FinanceApproveState"
 78}
 79
 80// GetFinanceApproveState GetFinanceApproveState
 81func GetFinanceApproveState() IState {
 82	return &financeApproveState{}
 83}
 84
 85func main() {
 86	m := &Machine{state: GetLeaderApproveState()}
 87	fmt.Println("LeaderApproveState", m.GetStateName())
 88	m.Approval()
 89	fmt.Println("FinanceApproveState", m.GetStateName())
 90	m.Reject()
 91	fmt.Println("LeaderApproveState", m.GetStateName())
 92	m.Approval()
 93	fmt.Println("FinanceApproveState", m.GetStateName())
 94	m.Approval()
 95}
 96
 97/*
 98LeaderApproveState LeaderApproveState
 99leader 审批成功
100FinanceApproveState FinanceApproveState
101LeaderApproveState LeaderApproveState
102leader 审批成功
103FinanceApproveState FinanceApproveState
104财务审批成功
105出发打款操作
106*/

迭代器模式

让你能在不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素。

代码实现

 1package main
 2
 3import "fmt"
 4
 5type collection interface {
 6	createIterator() iterator
 7}
 8
 9type userCollection struct {
10	users []*user
11}
12
13func (u *userCollection) createIterator() iterator {
14	return &userIterator{
15		users: u.users,
16	}
17}
18
19type iterator interface {
20	hasNext() bool
21	getNext() *user
22}
23
24type userIterator struct {
25	index int
26	users []*user
27}
28
29func (u *userIterator) hasNext() bool {
30	if u.index < len(u.users) {
31		return true
32	}
33	return false
34
35}
36func (u *userIterator) getNext() *user {
37	if u.hasNext() {
38		user := u.users[u.index]
39		u.index++
40		return user
41	}
42	return nil
43}
44
45type user struct {
46	name string
47	age  int
48}
49
50func main() {
51
52	user1 := &user{
53		name: "a",
54		age:  30,
55	}
56	user2 := &user{
57		name: "b",
58		age:  20,
59	}
60
61	userCollection := &userCollection{
62		users: []*user{user1, user2},
63	}
64
65	iterator := userCollection.createIterator()
66
67	for iterator.hasNext() {
68		user := iterator.getNext()
69		fmt.Printf("User is %+v\n", user)
70	}
71}
72
73/*
74User is &{name:a age:30}
75User is &{name:b age:20}
76*/

访问者模式

访问者模式可以给一系列对象透明的添加功能,并且把相关代码封装到一个类中。

对象只要预留访问者接口Accept则后期为对象添加功能的时候就不需要改动对象。

代码实现

 1package main
 2
 3import "fmt"
 4
 5type Customer interface {
 6	Accept(Visitor)
 7}
 8
 9type Visitor interface {
10	Visit(Customer)
11}
12
13type EnterpriseCustomer struct {
14	name string
15}
16
17type CustomerCol struct {
18	customers []Customer
19}
20
21func (c *CustomerCol) Add(customer Customer) {
22	c.customers = append(c.customers, customer)
23}
24
25func (c *CustomerCol) Accept(visitor Visitor) {
26	for _, customer := range c.customers {
27		customer.Accept(visitor)
28	}
29}
30
31func NewEnterpriseCustomer(name string) *EnterpriseCustomer {
32	return &EnterpriseCustomer{
33		name: name,
34	}
35}
36
37func (c *EnterpriseCustomer) Accept(visitor Visitor) {
38	visitor.Visit(c)
39}
40
41type IndividualCustomer struct {
42	name string
43}
44
45func NewIndividualCustomer(name string) *IndividualCustomer {
46	return &IndividualCustomer{
47		name: name,
48	}
49}
50
51func (c *IndividualCustomer) Accept(visitor Visitor) {
52	visitor.Visit(c)
53}
54
55type ServiceRequestVisitor struct{}
56
57func (*ServiceRequestVisitor) Visit(customer Customer) {
58	switch c := customer.(type) {
59	case *EnterpriseCustomer:
60		fmt.Printf("serving enterprise customer %s\n", c.name)
61	case *IndividualCustomer:
62		fmt.Printf("serving individual customer %s\n", c.name)
63	}
64}
65
66// only for enterprise
67type AnalysisVisitor struct{}
68
69func (*AnalysisVisitor) Visit(customer Customer) {
70	switch c := customer.(type) {
71	case *EnterpriseCustomer:
72		fmt.Printf("analysis enterprise customer %s\n", c.name)
73	}
74}
75
76func main() {
77	c := &CustomerCol{}
78	c.Add(NewEnterpriseCustomer("A company"))
79	c.Add(NewEnterpriseCustomer("B company"))
80	c.Add(NewIndividualCustomer("bob"))
81	c.Accept(&ServiceRequestVisitor{})
82
83	c = &CustomerCol{}
84	c.Add(NewEnterpriseCustomer("A company"))
85	c.Add(NewIndividualCustomer("bob"))
86	c.Add(NewEnterpriseCustomer("B company"))
87	c.Accept(&AnalysisVisitor{})
88}
89
90/*
91serving enterprise customer A company
92serving enterprise customer B company
93serving individual customer bob
94analysis enterprise customer A company
95analysis enterprise customer B company
96*/

备忘录模式

备忘录模式用于保存程序内部状态到外部,又不希望暴露内部状态的情形。

程序内部状态使用窄接口传递给外部进行存储,从而不暴露程序实现细节。

备忘录模式同时可以离线保存内部状态,如保存到数据库,文件等。

代码实现

 1package main
 2
 3import "fmt"
 4
 5type Memento interface{}
 6
 7type Game struct {
 8	hp, mp int
 9}
10
11type gameMemento struct {
12	hp, mp int
13}
14
15func (g *Game) Play(mpDelta, hpDelta int) {
16	g.mp += mpDelta
17	g.hp += hpDelta
18}
19
20func (g *Game) Save() Memento {
21	return &gameMemento{
22		hp: g.hp,
23		mp: g.mp,
24	}
25}
26
27func (g *Game) Load(m Memento) {
28	gm := m.(*gameMemento)
29	g.mp = gm.mp
30	g.hp = gm.hp
31}
32
33func (g *Game) Status() {
34	fmt.Printf("Current HP:%d, MP:%d\n", g.hp, g.mp)
35}
36
37func main() {
38	game := &Game{
39		hp: 10,
40		mp: 10,
41	}
42
43	game.Status()
44	progress := game.Save()
45
46	game.Play(-2, -3)
47	game.Status()
48
49	game.Load(progress)
50	game.Status()
51
52}
53
54/*
55Current HP:10, MP:10
56Current HP:7, MP:8
57Current HP:10, MP:10
58*/

解释器模式

解释器模式定义一套语言文法,并设计该语言解释器,使用户能使用特定文法控制解释器行为。

解释器模式的意义在于,它分离多种复杂功能的实现,每个功能只需关注自身的解释。

对于调用者不用关心内部的解释器的工作,只需要用简单的方式组合命令就可以。

代码实现

 1package main
 2
 3import (
 4	"fmt"
 5	"strconv"
 6	"strings"
 7)
 8
 9type Node interface {
10	Interpret() int
11}
12
13type ValNode struct {
14	val int
15}
16
17func (n *ValNode) Interpret() int {
18	return n.val
19}
20
21type AddNode struct {
22	left, right Node
23}
24
25func (n *AddNode) Interpret() int {
26	return n.left.Interpret() + n.right.Interpret()
27}
28
29type MinNode struct {
30	left, right Node
31}
32
33func (n *MinNode) Interpret() int {
34	return n.left.Interpret() - n.right.Interpret()
35}
36
37type Parser struct {
38	exp   []string
39	index int
40	prev  Node
41}
42
43func (p *Parser) Parse(exp string) {
44	p.exp = strings.Split(exp, " ")
45
46	for {
47		if p.index >= len(p.exp) {
48			return
49		}
50		switch p.exp[p.index] {
51		case "+":
52			p.prev = p.newAddNode()
53		case "-":
54			p.prev = p.newMinNode()
55		default:
56			p.prev = p.newValNode()
57		}
58	}
59}
60
61func (p *Parser) newAddNode() Node {
62	p.index++
63	return &AddNode{
64		left:  p.prev,
65		right: p.newValNode(),
66	}
67}
68
69func (p *Parser) newMinNode() Node {
70	p.index++
71	return &MinNode{
72		left:  p.prev,
73		right: p.newValNode(),
74	}
75}
76
77func (p *Parser) newValNode() Node {
78	v, _ := strconv.Atoi(p.exp[p.index])
79	p.index++
80	return &ValNode{
81		val: v,
82	}
83}
84
85func (p *Parser) Result() Node {
86	return p.prev
87}
88
89func main() {
90	p := &Parser{}
91	p.Parse("1 + 2 + 3 - 4 + 5 - 6")
92	res := p.Result().Interpret()
93	expect := 1
94	if res != expect {
95		fmt.Println(res,expect)
96	}
97}

中介模式

中介者模式封装对象之间互交,使依赖变的简单,并且使复杂互交简单化,封装在中介者中。

例子中的中介者使用单例模式生成中介者。

中介者的change使用switch判断类型。

代码实现

 1package main
 2
 3import (
 4	"fmt"
 5	"strings"
 6)
 7
 8type CDDriver struct {
 9	Data string
10}
11
12func (c *CDDriver) ReadData() {
13	c.Data = "music,image"
14
15	fmt.Printf("CDDriver: reading data %s\n", c.Data)
16	GetMediatorInstance().changed(c)
17}
18
19type CPU struct {
20	Video string
21	Sound string
22}
23
24func (c *CPU) Process(data string) {
25	sp := strings.Split(data, ",")
26	c.Sound = sp[0]
27	c.Video = sp[1]
28
29	fmt.Printf("CPU: split data with Sound %s, Video %s\n", c.Sound, c.Video)
30	GetMediatorInstance().changed(c)
31}
32
33type VideoCard struct {
34	Data string
35}
36
37func (v *VideoCard) Display(data string) {
38	v.Data = data
39	fmt.Printf("VideoCard: display %s\n", v.Data)
40	GetMediatorInstance().changed(v)
41}
42
43type SoundCard struct {
44	Data string
45}
46
47func (s *SoundCard) Play(data string) {
48	s.Data = data
49	fmt.Printf("SoundCard: play %s\n", s.Data)
50	GetMediatorInstance().changed(s)
51}
52
53type Mediator struct {
54	CD    *CDDriver
55	CPU   *CPU
56	Video *VideoCard
57	Sound *SoundCard
58}
59
60var mediator *Mediator
61
62func GetMediatorInstance() *Mediator {
63	if mediator == nil {
64		mediator = &Mediator{}
65	}
66	return mediator
67}
68
69func (m *Mediator) changed(i interface{}) {
70	switch inst := i.(type) {
71	case *CDDriver:
72		m.CPU.Process(inst.Data)
73	case *CPU:
74		m.Sound.Play(inst.Sound)
75		m.Video.Display(inst.Video)
76	}
77}
78
79func main() {
80	mediator := GetMediatorInstance()
81	mediator.CD = &CDDriver{}
82	mediator.CPU = &CPU{}
83	mediator.Video = &VideoCard{}
84	mediator.Sound = &SoundCard{}
85
86	//Tiggle
87	mediator.CD.ReadData()
88
89	fmt.Printf("%#v\n", mediator)
90}
91
92/*
93CDDriver: reading data music,image
94CPU: split data with Sound music, Video image
95SoundCard: play music
96VideoCard: display image
97&main.Mediator{CD:(*main.CDDriver)(0xc000010250), CPU:(*main.CPU)(0xc000060040), Video:(*main.VideoCard)(0xc000010260), Sound:(*main.SoundCard)(0xc000010270)}
98*/

References

https://github.com/senghoo/golang-design-pattern

https://refactoringguru.cn/design-patterns

https://lailin.xyz/post/singleton.html