Fundamentals 54 min read

Go Design Patterns: Comprehensive Examples and Implementations

This article showcases practical Go implementations of classic behavioral design patterns—Chain of Responsibility, Command, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, and Visitor—providing concise explanations, full source code, and test programs that demonstrate each pattern’s problem‑solving workflow.

Tencent Cloud Developer
Tencent Cloud Developer
Tencent Cloud Developer
Go Design Patterns: Comprehensive Examples and Implementations

Design patterns are a toolbox of proven solutions for common software design problems. This article presents classic Go implementations of several behavioral design patterns, providing concise explanations, code examples, test programs and execution results.

Chain of Responsibility Pattern

The Chain of Responsibility is a behavioral pattern that allows a request to be passed along a chain of handlers. Each handler can process the request or forward it to the next handler.

In the example, the boarding process of an airport is modeled. The request (Passenger) flows through handlers: boarding pass, luggage check‑in, identity check, security check, and final boarding.

package chainofresponsibility

import "fmt"

// BoardingProcessor 登机过程中,各节点统一处理接口
type BoardingProcessor interface {
  SetNextProcessor(processor BoardingProcessor)
  ProcessFor(passenger *Passenger)
}

// Passenger 旅客
type Passenger struct {
  name                  string // 姓名
  hasBoardingPass       bool   // 是否办理登机牌
  hasLuggage            bool   // 是否有行李需要托运
  isPassIdentityCheck   bool   // 是否通过身份校验
  isPassSecurityCheck   bool   // 是否通过安检
  isCompleteForBoarding bool   // 是否完成登机
}

// baseBoardingProcessor 登机流程处理器基类
type baseBoardingProcessor struct {
  nextProcessor BoardingProcessor // next processor
}

func (b *baseBoardingProcessor) SetNextProcessor(processor BoardingProcessor) {
  b.nextProcessor = processor
}

func (b *baseBoardingProcessor) ProcessFor(passenger *Passenger) {
  if b.nextProcessor != nil {
    b.nextProcessor.ProcessFor(passenger)
  }
}

// boardingPassProcessor 办理登机牌处理器
type boardingPassProcessor struct { baseBoardingProcessor }

func (b *boardingPassProcessor) ProcessFor(passenger *Passenger) {
  if !passenger.hasBoardingPass {
    fmt.Printf("为旅客%s办理登机牌;\n", passenger.name)
    passenger.hasBoardingPass = true
  }
  b.baseBoardingProcessor.ProcessFor(passenger)
}

// luggageCheckInProcessor 托运行李处理器
type luggageCheckInProcessor struct { baseBoardingProcessor }

func (l *luggageCheckInProcessor) ProcessFor(passenger *Passenger) {
  if !passenger.hasBoardingPass {
    fmt.Printf("旅客%s未办理登机牌,不能托运行李;\n", passenger.name)
    return
  }
  if passenger.hasLuggage {
    fmt.Printf("为旅客%s办理行李托运;\n", passenger.name)
  }
  l.baseBoardingProcessor.ProcessFor(passenger)
}

// identityCheckProcessor 校验身份处理器
type identityCheckProcessor struct { baseBoardingProcessor }

func (i *identityCheckProcessor) ProcessFor(passenger *Passenger) {
  if !passenger.hasBoardingPass {
    fmt.Printf("旅客%s未办理登机牌,不能办理身份校验;\n", passenger.name)
    return
  }
  if !passenger.isPassIdentityCheck {
    fmt.Printf("为旅客%s核实身份信息;\n", passenger.name)
    passenger.isPassIdentityCheck = true
  }
  i.baseBoardingProcessor.ProcessFor(passenger)
}

// securityCheckProcessor 安检处理器
type securityCheckProcessor struct { baseBoardingProcessor }

func (s *securityCheckProcessor) ProcessFor(passenger *Passenger) {
  if !passenger.hasBoardingPass {
    fmt.Printf("旅客%s未办理登机牌,不能进行安检;\n", passenger.name)
    return
  }
  if !passenger.isPassSecurityCheck {
    fmt.Printf("为旅客%s进行安检;\n", passenger.name)
    passenger.isPassSecurityCheck = true
  }
  s.baseBoardingProcessor.ProcessFor(passenger)
}

// completeBoardingProcessor 完成登机处理器
type completeBoardingProcessor struct { baseBoardingProcessor }

func (c *completeBoardingProcessor) ProcessFor(passenger *Passenger) {
  if !passenger.hasBoardingPass || !passenger.isPassIdentityCheck || !passenger.isPassSecurityCheck {
    fmt.Printf("旅客%s登机检查过程未完成,不能登机;\n", passenger.name)
    return
  }
  passenger.isCompleteForBoarding = true
  fmt.Printf("旅客%s成功登机;\n", passenger.name)
}

Test program builds the chain and runs it with a sample passenger.

package chainofresponsibility

import "testing"

func TestChainOfResponsibility(t *testing.T) {
  boardingProcessor := BuildBoardingProcessorChain()
  passenger := &Passenger{name: "李四", hasBoardingPass: false, hasLuggage: true, isPassIdentityCheck: false, isPassSecurityCheck: false, isCompleteForBoarding: false}
  boardingProcessor.ProcessFor(passenger)
}

func BuildBoardingProcessorChain() BoardingProcessor {
  complete := &completeBoardingProcessor{}
  security := &securityCheckProcessor{}
  security.SetNextProcessor(complete)
  identity := &identityCheckProcessor{}
  identity.SetNextProcessor(security)
  luggage := &luggageCheckInProcessor{}
  luggage.SetNextProcessor(identity)
  boarding := &boardingPassProcessor{}
  boarding.SetNextProcessor(luggage)
  return boarding
}

Running the test prints the step‑by‑step boarding process, demonstrating how the request flows through the chain.

Command Pattern

The Command pattern encapsulates a request as an object, allowing parameterization, queuing, logging, and undo operations.

The example models an electric cooker controlled by commands such as steam rice, cook congee, and shutdown.

package command

import "fmt"

type ElectricCooker struct { fire string; pressure string }
func (e *ElectricCooker) SetFire(f string) { e.fire = f }
func (e *ElectricCooker) SetPressure(p string) { e.pressure = p }
func (e *ElectricCooker) Run(d string) string { return fmt.Sprintf("电饭煲设置火力为%s,压力为%s,持续运行%s;", e.fire, e.pressure, d) }
func (e *ElectricCooker) Shutdown() string { return "电饭煲停止运行。" }

// Command interface
type CookCommand interface { Execute() string }

type steamRiceCommand struct { electricCooker *ElectricCooker }
func NewSteamRiceCommand(c *ElectricCooker) *steamRiceCommand { return &steamRiceCommand{electricCooker: c} }
func (s *steamRiceCommand) Execute() string {
  s.electricCooker.SetFire("中")
  s.electricCooker.SetPressure("正常")
  return "蒸饭:" + s.electricCooker.Run("30分钟")
}

type cookCongeeCommand struct { electricCooker *ElectricCooker }
func NewCookCongeeCommand(c *ElectricCooker) *cookCongeeCommand { return &cookCongeeCommand{electricCooker: c} }
func (c *cookCongeeCommand) Execute() string {
  c.electricCooker.SetFire("大")
  c.electricCooker.SetPressure("强")
  return "煮粥:" + c.electricCooker.Run("45分钟")
}

type shutdownCommand struct { electricCooker *ElectricCooker }
func NewShutdownCommand(c *ElectricCooker) *shutdownCommand { return &shutdownCommand{electricCooker: c} }
func (s *shutdownCommand) Execute() string { return s.electricCooker.Shutdown() }

// Invoker
type ElectricCookerInvoker struct { cookCommand CookCommand }
func (e *ElectricCookerInvoker) SetCookCommand(c CookCommand) { e.cookCommand = c }
func (e *ElectricCookerInvoker) ExecuteCookCommand() string { return e.cookCommand.Execute() }

Test program demonstrates the commands.

package command

import (
  "fmt"
  "testing"
)

func TestCommand(t *testing.T) {
  electricCooker := new(ElectricCooker)
  invoker := new(ElectricCookerInvoker)

  steam := NewSteamRiceCommand(electricCooker)
  invoker.SetCookCommand(steam)
  fmt.Println(invoker.ExecuteCookCommand())

  congee := NewCookCongeeCommand(electricCooker)
  invoker.SetCookCommand(congee)
  fmt.Println(invoker.ExecuteCookCommand())

  shut := NewShutdownCommand(electricCooker)
  invoker.SetCookCommand(shut)
  fmt.Println(invoker.ExecuteCookCommand())
}

Iterator Pattern

The Iterator pattern provides a way to access elements of a collection sequentially without exposing its underlying representation.

The example defines a Class containing a Teacher and a slice of Students, and provides an iterator that first returns the teacher then each student.

package iterator

import "fmt"

type Member interface { Desc() string }

type Teacher struct { name, subject string }
func NewTeacher(name, subject string) *Teacher { return &Teacher{name: name, subject: subject} }
func (t *Teacher) Desc() string { return fmt.Sprintf("%s班主任老师负责教%s", t.name, t.subject) }

type Student struct { name string; sumScore int }
func NewStudent(name string, sumScore int) *Student { return &Student{name: name, sumScore: sumScore} }
func (s *Student) Desc() string { return fmt.Sprintf("%s同学考试总分为%d", s.name, s.sumScore) }

type Iterator interface { Next() Member; HasMore() bool }

type memberIterator struct { class *Class; index int }
func (m *memberIterator) Next() Member {
  if m.index == -1 { m.index++; return m.class.teacher }
  student := m.class.students[m.index]
  m.index++
  return student
}
func (m *memberIterator) HasMore() bool { return m.index < len(m.class.students) }

type Iterable interface { CreateIterator() Iterator }

type Class struct { name string; teacher *Teacher; students []*Student }
func NewClass(name, teacherName, teacherSubject string) *Class {
  return &Class{name: name, teacher: NewTeacher(teacherName, teacherSubject)}
}
func (c *Class) AddStudent(students ...*Student) { c.students = append(c.students, students...) }
func (c *Class) CreateIterator() Iterator { return &memberIterator{class: c, index: -1} }
func (c *Class) Name() string { return c.name }

Test iterates over the class members.

package iterator

import (
  "fmt"
  "testing"
)

func TestIterator(t *testing.T) {
  class := NewClass("三年级一班", "王明", "数学课")
  class.AddStudent(NewStudent("张三", 389), NewStudent("李四", 378), NewStudent("王五", 347))
  fmt.Printf("%s成员如下:\n", class.Name())
  it := class.CreateIterator()
  for it.HasMore() {
    fmt.Println(it.Next().Desc())
  }
}

Mediator Pattern

The Mediator pattern reduces chaotic dependencies between objects by forcing them to communicate through a central mediator.

The example models an airport tower mediating landing and take‑off of aircraft.

package mediator

import "fmt"

type Aircraft interface { ApproachAirport(); DepartAirport() }

type airliner struct { name string; airportMediator AirportMediator }
func NewAirliner(name string, m AirportMediator) *airliner { return &airliner{name: name, airportMediator: m} }
func (a *airliner) ApproachAirport() {
  if !a.airportMediator.CanLandAirport(a) {
    fmt.Printf("机场繁忙,客机%s继续等待降落;\n", a.name)
    return
  }
  fmt.Printf("客机%s成功滑翔降落机场;\n", a.name)
}
func (a *airliner) DepartAirport() {
  fmt.Printf("客机%s成功滑翔起飞,离开机场;\n", a.name)
  a.airportMediator.NotifyWaitingAircraft()
}

type helicopter struct { name string; airportMediator AirportMediator }
func NewHelicopter(name string, m AirportMediator) *helicopter { return &helicopter{name: name, airportMediator: m} }
func (h *helicopter) ApproachAirport() {
  if !h.airportMediator.CanLandAirport(h) {
    fmt.Printf("机场繁忙,直升机%s继续等待降落;\n", h.name)
    return
  }
  fmt.Printf("直升机%s成功垂直降落机场;\n", h.name)
}
func (h *helicopter) DepartAirport() {
  fmt.Printf("直升机%s成功垂直起飞,离开机场;\n", h.name)
  h.airportMediator.NotifyWaitingAircraft()
}

type AirportMediator interface { CanLandAirport(a Aircraft) bool; NotifyWaitingAircraft() }

type ApproachTower struct { hasFreeAirstrip bool; waitingQueue []Aircraft }
func (a *ApproachTower) CanLandAirport(ac Aircraft) bool {
  if a.hasFreeAirstrip {
    a.hasFreeAirstrip = false
    return true
  }
  a.waitingQueue = append(a.waitingQueue, ac)
  return false
}
func (a *ApproachTower) NotifyWaitingAircraft() {
  if !a.hasFreeAirstrip { a.hasFreeAirstrip = true }
  if len(a.waitingQueue) > 0 {
    first := a.waitingQueue[0]
    a.waitingQueue = a.waitingQueue[1:]
    first.ApproachAirport()
  }
}

Test demonstrates aircraft landing, waiting, and take‑off.

package mediator

import "testing"

func TestMediator(t *testing.T) {
  tower := &ApproachTower{hasFreeAirstrip: true}
  c919 := NewAirliner("C919", tower)
  mi26 := NewHelicopter("米-26", tower)
  c919.ApproachAirport()
  mi26.ApproachAirport()
  c919.DepartAirport()
  mi26.DepartAirport()
}

Memento Pattern

The Memento pattern captures and restores an object's internal state without exposing its implementation details.

The example implements a simple RPG game with save‑and‑load functionality.

package memento

import "fmt"

type Originator interface { Save(tag string) Memento }

type RolesPlayGame struct { name string; rolesState []string; scenarioState string }
func NewRolesPlayGame(name, roleName string) *RolesPlayGame {
  return &RolesPlayGame{name: name, rolesState: []string{roleName, "血量100"}, scenarioState: "开始通过第一关"}
}
func (r *RolesPlayGame) Save(tag string) Memento { return newRPGArchive(tag, r.rolesState, r.scenarioState, r) }
func (r *RolesPlayGame) SetRolesState(s []string) { r.rolesState = s }
func (r *RolesPlayGame) SetScenarioState(s string) { r.scenarioState = s }
func (r *RolesPlayGame) String() string { return fmt.Sprintf("在%s游戏中,玩家使用%s,%s,%s;", r.name, r.rolesState[0], r.rolesState[1], r.scenarioState) }

type Memento interface { Tag() string; Restore() }

type rpgArchive struct { tag string; rolesState []string; scenarioState string; rpg *RolesPlayGame }
func newRPGArchive(tag string, rs []string, ss string, r *RolesPlayGame) *rpgArchive {
  return &rpgArchive{tag: tag, rolesState: rs, scenarioState: ss, rpg: r}
}
func (a *rpgArchive) Tag() string { return a.tag }
func (a *rpgArchive) Restore() { a.rpg.SetRolesState(a.rolesState); a.rpg.SetScenarioState(a.scenarioState) }

type RPGArchiveManager struct { archives map[string]Memento }
func NewRPGArchiveManager() *RPGArchiveManager { return &RPGArchiveManager{archives: make(map[string]Memento)} }
func (m *RPGArchiveManager) Put(memento Memento) { m.archives[memento.Tag()] = memento }
func (m *RPGArchiveManager) Reload(tag string) {
  if a, ok := m.archives[tag]; ok {
    fmt.Printf("重新加载%s;\n", tag)
    a.Restore()
  }
}

Test shows saving, modifying, and restoring a game state.

package memento

import (
  "fmt"
  "testing"
)

func TestMemento(t *testing.T) {
  manager := NewRPGArchiveManager()
  game := NewRolesPlayGame("暗黑破坏神2", "野蛮人战士")
  fmt.Println(game)
  manager.Put(game.Save("第一关存档"))

  game.SetRolesState([]string{"野蛮人战士", "死亡"})
  game.SetScenarioState("第一关闯关失败")
  fmt.Println(game)

  manager.Reload("第一关存档")
  fmt.Println(game)
}

Observer Pattern

The Observer pattern defines a subscription mechanism to notify multiple observers when an object changes.

The example models a credit‑card system where different notification channels (SMS, email, phone) subscribe to various message types.

package observer

import "fmt"

type Subscriber interface { Name() string; Update(message string) }

type shortMessage struct{}
func (s *shortMessage) Name() string { return "手机短息" }
func (s *shortMessage) Update(msg string) { fmt.Printf("通过【%s】发送消息:%s\n", s.Name(), msg) }

type email struct{}
func (e *email) Name() string { return "电子邮件" }
func (e *email) Update(msg string) { fmt.Printf("通过【%s】发送消息:%s\n", e.Name(), msg) }

type telephone struct{}
func (t *telephone) Name() string { return "电话" }
func (t *telephone) Update(msg string) { fmt.Printf("通过【%s】告知:%s\n", t.Name(), msg) }

type MsgType int
const (
  ConsumeType MsgType = iota
  BillType
  ExpireType
)

type CreditCard struct { holder string; consumeSum float32; subscriberGroup map[MsgType][]Subscriber }
func NewCreditCard(holder string) *CreditCard { return &CreditCard{holder: holder, subscriberGroup: make(map[MsgType][]Subscriber)} }
func (c *CreditCard) Subscribe(sub Subscriber, types ...MsgType) { for _, t := range types { c.subscriberGroup[t] = append(c.subscriberGroup[t], sub) } }
func (c *CreditCard) Unsubscribe(sub Subscriber, types ...MsgType) { for _, t := range types { if list, ok := c.subscriberGroup[t]; ok { c.subscriberGroup[t] = removeSubscriber(list, sub) } } }
func removeSubscriber(list []Subscriber, r Subscriber) []Subscriber {
  for i, s := range list { if s.Name() == r.Name() { list[i] = list[len(list)-1]; return list[:len(list)-1] } }
  return list
}
func (c *CreditCard) Consume(money float32) { c.consumeSum += money; c.notify(ConsumeType, fmt.Sprintf("尊敬的持卡人%s,您当前消费%.2f元;", c.holder, money)) }
func (c *CreditCard) SendBill() { c.notify(BillType, fmt.Sprintf("尊敬的持卡人%s,您本月账单已出,消费总额%.2f元;", c.holder, c.consumeSum)) }
func (c *CreditCard) Expire() { c.notify(ExpireType, fmt.Sprintf("尊敬的持卡人%s,您本月账单已逾期,请及时还款,总额%.2f元;", c.holder, c.consumeSum)) }
func (c *CreditCard) notify(t MsgType, msg string) { if subs, ok := c.subscriberGroup[t]; ok { for _, s := range subs { s.Update(msg) } } }

Test demonstrates subscription, notifications, and unsubscription.

package observer

import "testing"

func TestObserver(t *testing.T) {
  card := NewCreditCard("张三")
  card.Subscribe(new(shortMessage), ConsumeType, ExpireType)
  card.Subscribe(new(email), BillType, ExpireType)
  card.Subscribe(new(telephone), ExpireType)

  card.Consume(500.00)
  card.Consume(800.00)
  card.SendBill()
  card.Expire()

  card.Unsubscribe(new(email), ExpireType)
  card.Unsubscribe(new(shortMessage), ExpireType)
  card.Consume(300.00)
  card.Expire()
}

State Pattern

The State pattern allows an object to change its behavior when its internal state changes.

The example models an iPhone battery that can be in Full, Part, or Empty state, reacting to plug connect/disconnect events.

package state

import "fmt"

var (
  FullBatteryState  = new(fullBatteryState)
  EmptyBatteryState = new(emptyBatteryState)
  PartBatteryState  = new(partBatteryState)
)

type BatteryState interface { ConnectPlug(i *IPhone) string; DisconnectPlug(i *IPhone) string }

type fullBatteryState struct{}
func (s *fullBatteryState) String() string { return "满电状态" }
func (s *fullBatteryState) ConnectPlug(i *IPhone) string { return i.pauseCharge() }
func (s *fullBatteryState) DisconnectPlug(i *IPhone) string { i.SetBatteryState(PartBatteryState); return fmt.Sprintf("%s,%s转为%s", i.consume(), s, PartBatteryState) }

type emptyBatteryState struct{}
func (s *emptyBatteryState) String() string { return "没电状态" }
func (s *emptyBatteryState) ConnectPlug(i *IPhone) string { i.SetBatteryState(PartBatteryState); return fmt.Sprintf("%s,%s转为%s", i.charge(), s, PartBatteryState) }
func (s *emptyBatteryState) DisconnectPlug(i *IPhone) string { return i.shutdown() }

type partBatteryState struct{}
func (s *partBatteryState) String() string { return "有电状态" }
func (s *partBatteryState) ConnectPlug(i *IPhone) string { i.SetBatteryState(FullBatteryState); return fmt.Sprintf("%s,%s转为%s", i.charge(), s, FullBatteryState) }
func (s *partBatteryState) DisconnectPlug(i *IPhone) string { i.SetBatteryState(EmptyBatteryState); return fmt.Sprintf("%s,%s转为%s", i.consume(), s, EmptyBatteryState) }

type IPhone struct { model string; batteryState BatteryState }
func NewIPhone(model string) *IPhone { return &IPhone{model: model, batteryState: PartBatteryState} }
func (i *IPhone) BatteryState() string { return fmt.Sprintf("iPhone %s 当前为%s", i.model, i.batteryState) }
func (i *IPhone) ConnectPlug() string { return fmt.Sprintf("iPhone %s 连接电源线,%s", i.model, i.batteryState.ConnectPlug(i)) }
func (i *IPhone) DisconnectPlug() string { return fmt.Sprintf("iPhone %s 断开电源线,%s", i.model, i.batteryState.DisconnectPlug(i)) }
func (i *IPhone) SetBatteryState(s BatteryState) { i.batteryState = s }
func (i *IPhone) charge() string { return "正在充电" }
func (i *IPhone) pauseCharge() string { return "电已满,暂停充电" }
func (i *IPhone) shutdown() string { return "手机关闭" }
func (i *IPhone) consume() string { return "使用中,消耗电量" }

Test walks through charging, full‑charge protection, discharging, and shutdown.

package state

import (
  "fmt"
  "testing"
)

func TestState(t *testing.T) {
  phone := NewIPhone("13 pro")
  fmt.Println(phone.BatteryState())
  fmt.Println(phone.ConnectPlug())
  fmt.Println(phone.ConnectPlug())
  fmt.Println(phone.DisconnectPlug())
  fmt.Println(phone.DisconnectPlug())
  fmt.Println(phone.DisconnectPlug())
  fmt.Println(phone.ConnectPlug())
}

Strategy Pattern

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable.

The example shows seasonal weather for a city, where each season implements a different strategy.

package strategy

import "fmt"

type Season interface { ShowWeather(city string) string }

type spring struct { weathers map[string]string }
func NewSpring() *spring { return &spring{weathers: map[string]string{"北京": "干燥多风", "昆明": "清凉舒适"}} }
func (s *spring) ShowWeather(city string) string { return fmt.Sprintf("%s的春天,%s;", city, s.weathers[city]) }

type summer struct { weathers map[string]string }
func NewSummer() *summer { return &summer{weathers: map[string]string{"北京": "高温多雨", "昆明": "清凉舒适"}} }
func (s *summer) ShowWeather(city string) string { return fmt.Sprintf("%s的夏天,%s;", city, s.weathers[city]) }

type autumn struct { weathers map[string]string }
func NewAutumn() *autumn { return &autumn{weathers: map[string]string{"北京": "凉爽舒适", "昆明": "清凉舒适"}} }
func (a *autumn) ShowWeather(city string) string { return fmt.Sprintf("%s的秋天,%s;", city, a.weathers[city]) }

type winter struct { weathers map[string]string }
func NewWinter() *winter { return &winter{weathers: map[string]string{"北京": "干燥寒冷", "昆明": "清凉舒适"}} }
func (w *winter) ShowWeather(city string) string { return fmt.Sprintf("%s的冬天,%s;", city, w.weathers[city]) }

type City struct { name, feature string; season Season }
func NewCity(name, feature string) *City { return &City{name: name, feature: feature} }
func (c *City) SetSeason(s Season) { c.season = s }
func (c *City) String() string { return fmt.Sprintf("%s%s,%s", c.name, c.feature, c.season.ShowWeather(c.name)) }

Test cycles through the four seasons for Beijing.

package strategy

import (
  "fmt"
  "testing"
)

func TestStrategy(t *testing.T) {
  beijing := NewCity("北京", "四季分明")
  beijing.SetSeason(NewSpring())
  fmt.Println(beijing)
  beijing.SetSeason(NewSummer())
  fmt.Println(beijing)
  beijing.SetSeason(NewAutumn())
  fmt.Println(beijing)
  beijing.SetSeason(NewWinter())
  fmt.Println(beijing)
}

Template Method Pattern

The Template Method defines the skeleton of an algorithm in a base class, allowing subclasses to redefine certain steps.

The example models actors dressing with steps: makeup, clothing, accessories. Different actor types implement those steps.

package templatemethod

import (
  "bytes"
  "fmt"
)

type IActor interface { DressUp() string }

type dressBehavior interface { makeUp() string; clothe() string; wear() string }

type BaseActor struct { roleName string; dressBehavior }
func (b *BaseActor) DressUp() string {
  var buf bytes.Buffer
  buf.WriteString(fmt.Sprintf("扮演%s的", b.roleName))
  buf.WriteString(b.makeUp())
  buf.WriteString(b.clothe())
  buf.WriteString(b.wear())
  return buf.String()
}

type womanActor struct { BaseActor }
func NewWomanActor(role string) *womanActor {
  w := &womanActor{}
  w.roleName = role
  w.dressBehavior = w
  return w
}
func (w *womanActor) makeUp() string { return "女演员涂着口红,画着眉毛;" }
func (w *womanActor) clothe() string { return "穿着连衣裙;" }
func (w *womanActor) wear() string { return "带着耳环,手拎着包;" }

type manActor struct { BaseActor }
func NewManActor(role string) *manActor {
  m := &manActor{}
  m.roleName = role
  m.dressBehavior = m
  return m
}
func (m *manActor) makeUp() string { return "男演员刮净胡子,抹上发胶;" }
func (m *manActor) clothe() string { return "穿着一身西装;" }
func (m *manActor) wear() string { return "带上手表,抽着烟;" }

type childActor struct { BaseActor }
func NewChildActor(role string) *childActor {
  c := &childActor{}
  c.roleName = role
  c.dressBehavior = c
  return c
}
func (c *childActor) makeUp() string { return "儿童演员抹上红脸蛋;" }
func (c *childActor) clothe() string { return "穿着一身童装;" }
func (c *childActor) wear() string { return "手里拿着一串糖葫芦;" }

Test prints the dressing description for each actor.

package templatemethod

import (
  "fmt"
  "testing"
)

func TestTemplateMethod(t *testing.T) {
  showActors(NewWomanActor("妈妈"), NewManActor("爸爸"), NewChildActor("儿子"))
}

func showActors(actors ...IActor) {
  for _, a := range actors { fmt.Println(a.DressUp()) }
}

Visitor Pattern

The Visitor pattern separates an algorithm from the object structure it operates on, allowing new operations without modifying the objects.

The example models employees (product manager, software engineer, HR) and two visitors: one for KPI ranking and another for salary calculation.

package visitor

import "fmt"

type Employee interface { KPI() string; Accept(v EmployeeVisitor) }

type productManager struct { name string; productNum int; satisfaction int }
func NewProductManager(name string, pn, sat int) *productManager { return &productManager{name: name, productNum: pn, satisfaction: sat} }
func (p *productManager) KPI() string { return fmt.Sprintf("产品经理%s,上线%d个产品,平均满意度为%d", p.name, p.productNum, p.satisfaction) }
func (p *productManager) Accept(v EmployeeVisitor) { v.VisitProductManager(p) }

type softwareEngineer struct { name string; requirementNum int; bugNum int }
func NewSoftwareEngineer(name string, rn, bn int) *softwareEngineer { return &softwareEngineer{name: name, requirementNum: rn, bugNum: bn} }
func (s *softwareEngineer) KPI() string { return fmt.Sprintf("软件工程师%s,完成%d个需求,修复%d个问题", s.name, s.requirementNum, s.bugNum) }
func (s *softwareEngineer) Accept(v EmployeeVisitor) { v.VisitSoftwareEngineer(s) }

type hr struct { name string; recruitNum int }
func NewHR(name string, rn int) *hr { return &hr{name: name, recruitNum: rn} }
func (h *hr) KPI() string { return fmt.Sprintf("人力资源%s,招聘%d名员工", h.name, h.recruitNum) }
func (h *hr) Accept(v EmployeeVisitor) { v.VisitHR(h) }

// Visitor interface
type EmployeeVisitor interface {
  VisitProductManager(pm *productManager)
  VisitSoftwareEngineer(se *softwareEngineer)
  VisitHR(hr *hr)
}

type kpi struct { name string; sum int }

type kpiTopVisitor struct { top []*kpi }
func (v *kpiTopVisitor) VisitProductManager(pm *productManager) { v.top = append(v.top, &kpi{name: pm.name, sum: pm.productNum + pm.satisfaction}) }
func (v *kpiTopVisitor) VisitSoftwareEngineer(se *softwareEngineer) { v.top = append(v.top, &kpi{name: se.name, sum: se.requirementNum + se.bugNum}) }
func (v *kpiTopVisitor) VisitHR(hr *hr) { v.top = append(v.top, &kpi{name: hr.name, sum: hr.recruitNum}) }
func (v *kpiTopVisitor) Publish() {
  sort.Slice(v.top, func(i, j int) bool { return v.top[i].sum > v.top[j].sum })
  for i, k := range v.top { fmt.Printf("第%d名%s:完成KPI总数%d\n", i+1, k.name, k.sum) }
}

type salaryVisitor struct{}
func (s *salaryVisitor) VisitProductManager(pm *productManager) {
  fmt.Printf("产品经理基本薪资:1000元,KPI单位薪资:100元,%s,总工资为%d元\n", pm.KPI(), (pm.productNum+pm.satisfaction)*100+1000)
}
func (s *salaryVisitor) VisitSoftwareEngineer(se *softwareEngineer) {
  fmt.Printf("软件工程师基本薪资:1500元,KPI单位薪资:80元,%s,总工资为%d元\n", se.KPI(), (se.requirementNum+se.bugNum)*80+1500)
}
func (s *salaryVisitor) VisitHR(hr *hr) {
  fmt.Printf("人力资源基本薪资:800元,KPI单位薪资:120元,%s,总工资为%d元\n", hr.KPI(), hr.recruitNum*120+800)
}

Test creates employees, runs both visitors, and prints the KPI ranking and salaries.

package visitor

import "testing"

func TestVisitor(t *testing.T) {
  employees := AllEmployees()
  kpiVisitor := new(kpiTopVisitor)
  VisitAllEmployees(kpiVisitor, employees)
  kpiVisitor.Publish()

  salaryVis := new(salaryVisitor)
  VisitAllEmployees(salaryVis, employees)
}

func VisitAllEmployees(v EmployeeVisitor, list []Employee) { for _, e := range list { e.Accept(v) } }

func AllEmployees() []Employee {
  var emps []Employee
  emps = append(emps, NewHR("小明", 10))
  emps = append(emps, NewProductManager("小红", 4, 7))
  emps = append(emps, NewSoftwareEngineer("张三", 10, 5))
  emps = append(emps, NewSoftwareEngineer("李四", 3, 6))
  emps = append(emps, NewSoftwareEngineer("王五", 7, 1))
  return emps
}

Conclusion

The article provides a practical catalog of Go implementations for many classic behavioral design patterns, illustrating how each pattern solves specific design problems and how the code can be tested and verified.

Design Patternssoftware-architectureProgrammingGocode examplesBehavioral Patterns
Tencent Cloud Developer
Written by

Tencent Cloud Developer

Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.