行为模式
责任链模式
责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。比如 kratos
,gin
等开源库的中间件实现。
代码实现
package main
import (
"context"
"fmt"
)
type Handler func(ctx context.Context, req interface{}) (resp interface{}, err error)
type Middleware func(next Handler) Handler
func Chain(middlewares ...Middleware) Middleware {
return func(next Handler) Handler {
for i := len(middlewares) - 1; i >= 0; i-- {
next = middlewares[i](next)
}
return next
}
}
func main() {
c := Chain(func(next Handler) Handler {
return func(ctx context.Context, req interface{}) (resp interface{}, err error) {
fmt.Println("handler 1 before")
resp, err = next(ctx, req)
fmt.Println("handler 1 after")
return resp, err
}
}, func(next Handler) Handler {
return func(ctx context.Context, req interface{}) (resp interface{}, err error) {
fmt.Println("handler 2 before")
resp, err = next(ctx, req)
fmt.Println("handler 2 after")
return resp, err
}
})
resp, err := c(func(ctx context.Context, req interface{}) (resp interface{}, err error) {
fmt.Println("handler req:", req)
return req, nil
})(context.Background(), "hello")
fmt.Println(resp, err)
}
/*
handler 1 before
handler 2 before
handler req: hello
handler 2 after
handler 1 after
hello <nil>
*/
观察者模式
观察者模式用于触发联动。一个对象的改变会触发其它观察者的相关动作,而此对象无需关心连动对象的具体实现。
代码实现
package main
import "fmt"
type subject interface {
register(Observer observer)
deregister(Observer observer)
notifyAll()
}
type observer interface {
update(string)
getID() string
}
type item struct {
observerList []observer
name string
inStock bool
}
func newItem(name string) *item {
return &item{
name: name,
}
}
func (i *item) updateAvailability() {
fmt.Printf("Item %s is now in stock\n", i.name)
i.inStock = true
i.notifyAll()
}
func (i *item) register(o observer) {
i.observerList = append(i.observerList, o)
}
func (i *item) deregister(o observer) {
i.observerList = removeFromslice(i.observerList, o)
}
func (i *item) notifyAll() {
for _, observer := range i.observerList {
observer.update(i.name)
}
}
func removeFromslice(observerList []observer, observerToRemove observer) []observer {
observerListLength := len(observerList)
for i, observer := range observerList {
if observerToRemove.getID() == observer.getID() {
observerList[observerListLength-1], observerList[i] = observerList[i], observerList[observerListLength-1]
return observerList[:observerListLength-1]
}
}
return observerList
}
type customer struct {
id string
}
func (c *customer) update(itemName string) {
fmt.Printf("Sending email to customer %s for item %s\n", c.id, itemName)
}
func (c *customer) getID() string {
return c.id
}
func main() {
shirtItem := newItem("Nike Shirt")
observerFirst := &customer{id: "abc@gmail.com"}
observerSecond := &customer{id: "xyz@gmail.com"}
shirtItem.register(observerFirst)
shirtItem.register(observerSecond)
shirtItem.updateAvailability()
}
/*
Item Nike Shirt is now in stock
Sending email to customer abc@gmail.com for item Nike Shirt
Sending email to customer xyz@gmail.com for item Nike Shirt
*/
模板方法模式
模版方法模式使用继承机制,把通用步骤和通用方法放到父类中,把具体实现延迟到子类中实现。使得实现符合开闭原则。
如实例代码中通用步骤在父类中实现(准备
、下载
、保存
、收尾
)下载和保存的具体实现留到子类中,并且提供 保存
方法的默认实现。
因为Golang不提供继承机制,需要使用匿名组合模拟实现继承。
此处需要注意:因为父类需要调用子类方法,所以子类需要匿名组合父类的同时,父类需要持有子类的引用。
代码实现
package main
import "fmt"
type Downloader interface {
Download(uri string)
}
type template struct {
implement
uri string
}
type implement interface {
download()
save()
}
func newTemplate(impl implement) *template {
return &template{
implement: impl,
}
}
func (t *template) Download(uri string) {
t.uri = uri
fmt.Print("prepare downloading\n")
t.implement.download()
t.implement.save()
fmt.Print("finish downloading\n")
}
func (t *template) save() {
fmt.Print("default save\n")
}
type HTTPDownloader struct {
*template
}
func NewHTTPDownloader() Downloader {
downloader := &HTTPDownloader{}
template := newTemplate(downloader)
downloader.template = template
return downloader
}
func (d *HTTPDownloader) download() {
fmt.Printf("download %s via http\n", d.uri)
}
func (*HTTPDownloader) save() {
fmt.Printf("http save\n")
}
type FTPDownloader struct {
*template
}
func NewFTPDownloader() Downloader {
downloader := &FTPDownloader{}
template := newTemplate(downloader)
downloader.template = template
return downloader
}
func (d *FTPDownloader) download() {
fmt.Printf("download %s via ftp\n", d.uri)
}
func main() {
downloader := NewHTTPDownloader()
downloader.Download("http://example.com/abc.zip")
downloader = NewFTPDownloader()
downloader.Download("ftp://example.com/abc.zip")
}
/*
prepare downloading
download http://example.com/abc.zip via http
http save
finish downloading
prepare downloading
download ftp://example.com/abc.zip via ftp
default save
finish downloading
*/
命令模式
命令模式是一种行为设计模式, 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中, 且能实现可撤销操作。
命令模式本质是把某个对象的方法调用封装到对象中,方便传递、存储、调用。
示例中把主板单中的启动(start)方法和重启(reboot)方法封装为命令对象,再传递到主机(box)对象中。于两个按钮进行绑定:
- 第一个机箱(box1)设置按钮1(button1) 为开机按钮2(button2)为重启。
- 第二个机箱(box1)设置按钮2(button2) 为开机按钮1(button1)为重启。
从而得到配置灵活性。
除了配置灵活外,使用命令模式还可以用作:
- 批处理
- 任务队列
- undo, redo
等把具体命令封装到对象中使用的场合
代码实现
package command
import "fmt"
type Command interface {
Execute()
}
type StartCommand struct {
mb *MotherBoard
}
func NewStartCommand(mb *MotherBoard) *StartCommand {
return &StartCommand{
mb: mb,
}
}
func (c *StartCommand) Execute() {
c.mb.Start()
}
type RebootCommand struct {
mb *MotherBoard
}
func NewRebootCommand(mb *MotherBoard) *RebootCommand {
return &RebootCommand{
mb: mb,
}
}
func (c *RebootCommand) Execute() {
c.mb.Reboot()
}
type MotherBoard struct{}
func (*MotherBoard) Start() {
fmt.Print("system starting\n")
}
func (*MotherBoard) Reboot() {
fmt.Print("system rebooting\n")
}
type Box struct {
button1 Command
button2 Command
}
func NewBox(button1, button2 Command) *Box {
return &Box{
button1: button1,
button2: button2,
}
}
func (b *Box) PressButton1() {
b.button1.Execute()
}
func (b *Box) PressButton2() {
b.button2.Execute()
}
策略模式
它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。
代码实现
package main
import "fmt"
type Payment struct {
context *PaymentContext
strategy PaymentStrategy
}
type PaymentContext struct {
Name, CardID string
Money int
}
func NewPayment(name, cardid string, money int, strategy PaymentStrategy) *Payment {
return &Payment{
context: &PaymentContext{
Name: name,
CardID: cardid,
Money: money,
},
strategy: strategy,
}
}
func (p *Payment) Pay() {
p.strategy.Pay(p.context)
}
type PaymentStrategy interface {
Pay(*PaymentContext)
}
type Cash struct{}
func (*Cash) Pay(ctx *PaymentContext) {
fmt.Printf("Pay $%d to %s by cash\n", ctx.Money, ctx.Name)
}
type Bank struct{}
func (*Bank) Pay(ctx *PaymentContext) {
fmt.Printf("Pay $%d to %s by bank account %s\n", ctx.Money, ctx.Name, ctx.CardID)
}
func main() {
payment := NewPayment("Ada", "", 123, &Cash{})
payment.Pay()
payment = NewPayment("Bob", "0002", 888, &Bank{})
payment.Pay()
}
/*
Pay $123 to Ada by cash
Pay $888 to Bob by bank account 0002
*/
状态模式
让你能在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。
代码实现
package main
import (
"fmt"
)
// Machine 状态机
type Machine struct {
state IState
}
// SetState 更新状态
func (m *Machine) SetState(state IState) {
m.state = state
}
// GetStateName 获取当前状态
func (m *Machine) GetStateName() string {
return m.state.GetName()
}
func (m *Machine) Approval() {
m.state.Approval(m)
}
func (m *Machine) Reject() {
m.state.Reject(m)
}
// IState 状态
type IState interface {
// 审批通过
Approval(m *Machine)
// 驳回
Reject(m *Machine)
// 获取当前状态名称
GetName() string
}
// leaderApproveState 直属领导审批
type leaderApproveState struct{}
// Approval 获取状态名字
func (leaderApproveState) Approval(m *Machine) {
fmt.Println("leader 审批成功")
m.SetState(GetFinanceApproveState())
}
// GetName 获取状态名字
func (leaderApproveState) GetName() string {
return "LeaderApproveState"
}
// Reject 获取状态名字
func (leaderApproveState) Reject(m *Machine) {}
func GetLeaderApproveState() IState {
return &leaderApproveState{}
}
// financeApproveState 财务审批
type financeApproveState struct{}
// Approval 审批通过
func (f financeApproveState) Approval(m *Machine) {
fmt.Println("财务审批成功")
fmt.Println("出发打款操作")
}
// 拒绝
func (f financeApproveState) Reject(m *Machine) {
m.SetState(GetLeaderApproveState())
}
// GetName 获取名字
func (f financeApproveState) GetName() string {
return "FinanceApproveState"
}
// GetFinanceApproveState GetFinanceApproveState
func GetFinanceApproveState() IState {
return &financeApproveState{}
}
func main() {
m := &Machine{state: GetLeaderApproveState()}
fmt.Println("LeaderApproveState", m.GetStateName())
m.Approval()
fmt.Println("FinanceApproveState", m.GetStateName())
m.Reject()
fmt.Println("LeaderApproveState", m.GetStateName())
m.Approval()
fmt.Println("FinanceApproveState", m.GetStateName())
m.Approval()
}
/*
LeaderApproveState LeaderApproveState
leader 审批成功
FinanceApproveState FinanceApproveState
LeaderApproveState LeaderApproveState
leader 审批成功
FinanceApproveState FinanceApproveState
财务审批成功
出发打款操作
*/
迭代器模式
让你能在不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素。
代码实现
package main
import "fmt"
type collection interface {
createIterator() iterator
}
type userCollection struct {
users []*user
}
func (u *userCollection) createIterator() iterator {
return &userIterator{
users: u.users,
}
}
type iterator interface {
hasNext() bool
getNext() *user
}
type userIterator struct {
index int
users []*user
}
func (u *userIterator) hasNext() bool {
if u.index < len(u.users) {
return true
}
return false
}
func (u *userIterator) getNext() *user {
if u.hasNext() {
user := u.users[u.index]
u.index++
return user
}
return nil
}
type user struct {
name string
age int
}
func main() {
user1 := &user{
name: "a",
age: 30,
}
user2 := &user{
name: "b",
age: 20,
}
userCollection := &userCollection{
users: []*user{user1, user2},
}
iterator := userCollection.createIterator()
for iterator.hasNext() {
user := iterator.getNext()
fmt.Printf("User is %+v\n", user)
}
}
/*
User is &{name:a age:30}
User is &{name:b age:20}
*/
访问者模式
访问者模式可以给一系列对象透明的添加功能,并且把相关代码封装到一个类中。
对象只要预留访问者接口Accept
则后期为对象添加功能的时候就不需要改动对象。
代码实现
package main
import "fmt"
type Customer interface {
Accept(Visitor)
}
type Visitor interface {
Visit(Customer)
}
type EnterpriseCustomer struct {
name string
}
type CustomerCol struct {
customers []Customer
}
func (c *CustomerCol) Add(customer Customer) {
c.customers = append(c.customers, customer)
}
func (c *CustomerCol) Accept(visitor Visitor) {
for _, customer := range c.customers {
customer.Accept(visitor)
}
}
func NewEnterpriseCustomer(name string) *EnterpriseCustomer {
return &EnterpriseCustomer{
name: name,
}
}
func (c *EnterpriseCustomer) Accept(visitor Visitor) {
visitor.Visit(c)
}
type IndividualCustomer struct {
name string
}
func NewIndividualCustomer(name string) *IndividualCustomer {
return &IndividualCustomer{
name: name,
}
}
func (c *IndividualCustomer) Accept(visitor Visitor) {
visitor.Visit(c)
}
type ServiceRequestVisitor struct{}
func (*ServiceRequestVisitor) Visit(customer Customer) {
switch c := customer.(type) {
case *EnterpriseCustomer:
fmt.Printf("serving enterprise customer %s\n", c.name)
case *IndividualCustomer:
fmt.Printf("serving individual customer %s\n", c.name)
}
}
// only for enterprise
type AnalysisVisitor struct{}
func (*AnalysisVisitor) Visit(customer Customer) {
switch c := customer.(type) {
case *EnterpriseCustomer:
fmt.Printf("analysis enterprise customer %s\n", c.name)
}
}
func main() {
c := &CustomerCol{}
c.Add(NewEnterpriseCustomer("A company"))
c.Add(NewEnterpriseCustomer("B company"))
c.Add(NewIndividualCustomer("bob"))
c.Accept(&ServiceRequestVisitor{})
c = &CustomerCol{}
c.Add(NewEnterpriseCustomer("A company"))
c.Add(NewIndividualCustomer("bob"))
c.Add(NewEnterpriseCustomer("B company"))
c.Accept(&AnalysisVisitor{})
}
/*
serving enterprise customer A company
serving enterprise customer B company
serving individual customer bob
analysis enterprise customer A company
analysis enterprise customer B company
*/
备忘录模式
备忘录模式用于保存程序内部状态到外部,又不希望暴露内部状态的情形。
程序内部状态使用窄接口传递给外部进行存储,从而不暴露程序实现细节。
备忘录模式同时可以离线保存内部状态,如保存到数据库,文件等。
代码实现
package main
import "fmt"
type Memento interface{}
type Game struct {
hp, mp int
}
type gameMemento struct {
hp, mp int
}
func (g *Game) Play(mpDelta, hpDelta int) {
g.mp += mpDelta
g.hp += hpDelta
}
func (g *Game) Save() Memento {
return &gameMemento{
hp: g.hp,
mp: g.mp,
}
}
func (g *Game) Load(m Memento) {
gm := m.(*gameMemento)
g.mp = gm.mp
g.hp = gm.hp
}
func (g *Game) Status() {
fmt.Printf("Current HP:%d, MP:%d\n", g.hp, g.mp)
}
func main() {
game := &Game{
hp: 10,
mp: 10,
}
game.Status()
progress := game.Save()
game.Play(-2, -3)
game.Status()
game.Load(progress)
game.Status()
}
/*
Current HP:10, MP:10
Current HP:7, MP:8
Current HP:10, MP:10
*/
解释器模式
解释器模式定义一套语言文法,并设计该语言解释器,使用户能使用特定文法控制解释器行为。
解释器模式的意义在于,它分离多种复杂功能的实现,每个功能只需关注自身的解释。
对于调用者不用关心内部的解释器的工作,只需要用简单的方式组合命令就可以。
代码实现
package main
import (
"fmt"
"strconv"
"strings"
)
type Node interface {
Interpret() int
}
type ValNode struct {
val int
}
func (n *ValNode) Interpret() int {
return n.val
}
type AddNode struct {
left, right Node
}
func (n *AddNode) Interpret() int {
return n.left.Interpret() + n.right.Interpret()
}
type MinNode struct {
left, right Node
}
func (n *MinNode) Interpret() int {
return n.left.Interpret() - n.right.Interpret()
}
type Parser struct {
exp []string
index int
prev Node
}
func (p *Parser) Parse(exp string) {
p.exp = strings.Split(exp, " ")
for {
if p.index >= len(p.exp) {
return
}
switch p.exp[p.index] {
case "+":
p.prev = p.newAddNode()
case "-":
p.prev = p.newMinNode()
default:
p.prev = p.newValNode()
}
}
}
func (p *Parser) newAddNode() Node {
p.index++
return &AddNode{
left: p.prev,
right: p.newValNode(),
}
}
func (p *Parser) newMinNode() Node {
p.index++
return &MinNode{
left: p.prev,
right: p.newValNode(),
}
}
func (p *Parser) newValNode() Node {
v, _ := strconv.Atoi(p.exp[p.index])
p.index++
return &ValNode{
val: v,
}
}
func (p *Parser) Result() Node {
return p.prev
}
func main() {
p := &Parser{}
p.Parse("1 + 2 + 3 - 4 + 5 - 6")
res := p.Result().Interpret()
expect := 1
if res != expect {
fmt.Println(res,expect)
}
}
中介模式
中介者模式封装对象之间互交,使依赖变的简单,并且使复杂互交简单化,封装在中介者中。
例子中的中介者使用单例模式生成中介者。
中介者的change使用switch判断类型。
代码实现
package main
import (
"fmt"
"strings"
)
type CDDriver struct {
Data string
}
func (c *CDDriver) ReadData() {
c.Data = "music,image"
fmt.Printf("CDDriver: reading data %s\n", c.Data)
GetMediatorInstance().changed(c)
}
type CPU struct {
Video string
Sound string
}
func (c *CPU) Process(data string) {
sp := strings.Split(data, ",")
c.Sound = sp[0]
c.Video = sp[1]
fmt.Printf("CPU: split data with Sound %s, Video %s\n", c.Sound, c.Video)
GetMediatorInstance().changed(c)
}
type VideoCard struct {
Data string
}
func (v *VideoCard) Display(data string) {
v.Data = data
fmt.Printf("VideoCard: display %s\n", v.Data)
GetMediatorInstance().changed(v)
}
type SoundCard struct {
Data string
}
func (s *SoundCard) Play(data string) {
s.Data = data
fmt.Printf("SoundCard: play %s\n", s.Data)
GetMediatorInstance().changed(s)
}
type Mediator struct {
CD *CDDriver
CPU *CPU
Video *VideoCard
Sound *SoundCard
}
var mediator *Mediator
func GetMediatorInstance() *Mediator {
if mediator == nil {
mediator = &Mediator{}
}
return mediator
}
func (m *Mediator) changed(i interface{}) {
switch inst := i.(type) {
case *CDDriver:
m.CPU.Process(inst.Data)
case *CPU:
m.Sound.Play(inst.Sound)
m.Video.Display(inst.Video)
}
}
func main() {
mediator := GetMediatorInstance()
mediator.CD = &CDDriver{}
mediator.CPU = &CPU{}
mediator.Video = &VideoCard{}
mediator.Sound = &SoundCard{}
//Tiggle
mediator.CD.ReadData()
fmt.Printf("%#v\n", mediator)
}
/*
CDDriver: reading data music,image
CPU: split data with Sound music, Video image
SoundCard: play music
VideoCard: display image
&main.Mediator{CD:(*main.CDDriver)(0xc000010250), CPU:(*main.CPU)(0xc000060040), Video:(*main.VideoCard)(0xc000010260), Sound:(*main.SoundCard)(0xc000010270)}
*/
References
https://github.com/senghoo/golang-design-pattern