Go 设计模式速查手册

Go 设计模式速查手册 整理自 Refactoring.Guru - Design Patterns in Go (https://refactoring.guru/design-patterns/go) --- 目录 - 创建型模式 (Creational Patterns) ( 创建型模式-creational-patterns) - 1. 单例模式 (

整理自 [Refactoring.Guru - Design Patterns in Go](https://refactoring.guru/design-patterns/go)


目录

  • 创建型模式 (Creational Patterns)

      1. 单例模式 (Singleton)
      1. 工厂方法模式 (Factory Method)
      1. 抽象工厂模式 (Abstract Factory)
      1. 建造者模式 (Builder)
      1. 原型模式 (Prototype)
  • 结构型模式 (Structural Patterns)

      1. 适配器模式 (Adapter)
      1. 桥接模式 (Bridge)
      1. 组合模式 (Composite)
      1. 装饰器模式 (Decorator)
      1. 外观模式 (Facade)
      1. 享元模式 (Flyweight)
      1. 代理模式 (Proxy)
  • 行为型模式 (Behavioral Patterns)

      1. 责任链模式 (Chain of Responsibility)
      1. 命令模式 (Command)
      1. 迭代器模式 (Iterator)
      1. 中介者模式 (Mediator)
      1. 备忘录模式 (Memento)
      1. 观察者模式 (Observer)
      1. 状态模式 (State)
      1. 策略模式 (Strategy)
      1. 模板方法模式 (Template Method)
      1. 访问者模式 (Visitor)

创建型模式 (Creational Patterns)

1. 单例模式 (Singleton)

意图: 确保一个类只有一个实例,并提供一个全局访问点。

说明: 单例与全局变量几乎有相同的优缺点。虽然它们非常方便,但会破坏代码的模块化。

Go 实现要点:

  • 使用 sync.Mutex 进行双重检查锁定(Double-Check Locking)
  • 或使用 sync.Once 确保只执行一次

方式一: `sync.Mutex + 双重检查锁定

 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
package main

import (
    "fmt"
    "sync"
)

var lock = &sync.Mutex{}

type single struct{}

var singleInstance *single

func getInstance() *single {
    if singleInstance == nil {
        lock.Lock()
        defer lock.Unlock()
        if singleInstance == nil {
            fmt.Println("Creating single instance now.")
            singleInstance = &single{}
        } else {
            fmt.Println("Single instance already created.")
        }
    } else {
        fmt.Println("Single instance already created.")
    }
    return singleInstance
}

方式二: sync.Once (推荐)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
    "fmt"
    "sync"
)

var once sync.Once

type single struct{}

var singleInstance *single

func getInstance() *single {
    if singleInstance == nil {
        once.Do(func() {
            fmt.Println("Creating single instance now.")
            singleInstance = &single{}
        })
    } else {
        fmt.Println("Single instance already created.")
    }
    return singleInstance
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package main

import "fmt"

func main() {
    for i := 0; i < 30; i++ {
        go getInstance()
    }
    fmt.Scanln()
}

输出:

1
2
3
4
Creating single instance now.
Single instance already created.
Single instance already created.
...

2. 工厂方法模式 (Factory Method)

意图: 提供一种创建对象的接口,让子类决定实例化哪个类。

说明: 由于 Go 缺乏类和继承等 OOP 特性,无法实现经典的工厂方法模式。但可以实现简单工厂(Simple Factory)版本。

示例: 使用工厂构建各种类型的武器。

iGun.go - 产品接口

1
2
3
4
5
6
7
8
package main

type IGun interface {
    setName(name string)
    setPower(power int)
    getName() string
    getPower() int
}

gun.go - 具体产品基类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

type Gun struct {
    name  string
    power int
}

func (g *Gun) setName(name string) {
    g.name = name
}

func (g *Gun) getName() string {
    return g.name
}

func (g *Gun) setPower(power int) {
    g.power = power
}

func (g *Gun) getPower() int {
    return g.power
}

ak47.go - 具体产品

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

type Ak47 struct {
    Gun
}

func newAk47() IGun {
    return &Ak47{
        Gun: Gun{
            name:  "AK47 gun",
            power: 4,
        },
    }
}

musket.go - 具体产品

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

type musket struct {
    Gun
}

func newMusket() IGun {
    return &musket{
        Gun: Gun{
            name:  "Musket gun",
            power: 1,
        },
    }
}

gunFactory.go - 工厂

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import "fmt"

func getGun(gunType string) (IGun, error) {
    if gunType == "ak47" {
        return newAk47(), nil
    }
    if gunType == "musket" {
        return newMusket(), nil
    }
    return nil, fmt.Errorf("Wrong gun type passed")
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func main() {
    ak47, _ := getGun("ak47")
    musket, _ := getGun("musket")

    printDetails(ak47)
    printDetails(musket)
}

func printDetails(g IGun) {
    fmt.Printf("Gun: %s
", g.getName())
    fmt.Printf("Power: %d
", g.getPower())
}

输出:

1
2
3
4
Gun: AK47 gun
Power: 4
Gun: Musket gun
Power: 1

3. 抽象工厂模式 (Abstract Factory)

意图: 创建相关对象家族,而不需要指定它们的具体类。

示例: 购买运动套装(鞋子和衬衫),需要同一品牌的产品互相匹配。

iSportsFactory.go - 抽象工厂接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

type ISportsFactory interface {
    makeShoe() IShoe
    makeShirt() IShirt
}

func GetSportsFactory(brand string) (ISportsFactory, error) {
    if brand == "adidas" {
        return &Adidas{}, nil
    }
    if brand == "nike" {
        return &Nike{}, nil
    }
    return nil, fmt.Errorf("Wrong brand type passed")
}

adidas.go / nike.go - 具体工厂

 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
package main

// Adidas 具体工厂
type Adidas struct{}

func (a *Adidas) makeShoe() IShoe {
    return &AdidasShoe{
        Shoe: Shoe{logo: "adidas", size: 14},
    }
}

func (a *Adidas) makeShirt() IShirt {
    return &AdidasShirt{
        Shirt: Shirt{logo: "adidas", size: 14},
    }
}

// Nike 具体工厂
type Nike struct{}

func (n *Nike) makeShoe() IShoe {
    return &NikeShoe{
        Shoe: Shoe{logo: "nike", size: 14},
    }
}

func (n *Nike) makeShirt() IShirt {
    return &NikeShirt{
        Shirt: Shirt{logo: "nike", size: 14},
    }
}

iShoe.go / iShirt.go - 抽象产品

 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
package main

// 鞋子接口
type IShoe interface {
    setLogo(logo string)
    setSize(size int)
    getLogo() string
    getSize() int
}

type Shoe struct {
    logo string
    size int
}

func (s *Shoe) setLogo(logo string) { s.logo = logo }
func (s *Shoe) getLogo() string     { return s.logo }
func (s *Shoe) setSize(size int)    { s.size = size }
func (s *Shoe) getSize() int        { return s.size }

// 衬衫接口
type IShirt interface {
    setLogo(logo string)
    setSize(size int)
    getLogo() string
    getSize() int
}

type Shirt struct {
    logo string
    size int
}

func (s *Shirt) setLogo(logo string) { s.logo = logo }
func (s *Shirt) getLogo() string     { return s.logo }
func (s *Shirt) setSize(size int)    { s.size = size }
func (s *Shirt) getSize() int        { return s.size }

具体产品

1
2
3
4
5
6
7
package main

// 具体产品通过嵌入基类实现接口
type AdidasShoe struct{ Shoe }
type NikeShoe struct{ Shoe }
type AdidasShirt struct{ Shirt }
type NikeShirt struct{ Shirt }

main.go

 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
package main

import "fmt"

func main() {
    adidasFactory, _ := GetSportsFactory("adidas")
    nikeFactory, _ := GetSportsFactory("nike")

    nikeShoe := nikeFactory.makeShoe()
    nikeShirt := nikeFactory.makeShirt()
    adidasShoe := adidasFactory.makeShoe()
    adidasShirt := adidasFactory.makeShirt()

    printShoeDetails(nikeShoe)
    printShirtDetails(nikeShirt)
    printShoeDetails(adidasShoe)
    printShirtDetails(adidasShirt)
}

func printShoeDetails(s IShoe) {
    fmt.Printf("Logo: %s
Size: %d
", s.getLogo(), s.getSize())
}

func printShirtDetails(s IShirt) {
    fmt.Printf("Logo: %s
Size: %d
", s.getLogo(), s.getSize())
}

4. 建造者模式 (Builder)

意图: 允许你逐步构建复杂对象。该模式允许你使用相同的构造代码生成不同类型和表示的对象。

示例: 构建不同类型的房子(普通房子和爱斯基摩人冰屋)。

iBuilder.go - 建造者接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

type IBuilder interface {
    setWindowType()
    setDoorType()
    setNumFloor()
    getHouse() House
}

func getBuilder(builderType string) IBuilder {
    if builderType == "normal" {
        return newNormalBuilder()
    }
    if builderType == "igloo" {
        return newIglooBuilder()
    }
    return nil
}

normalBuilder.go / iglooBuilder.go - 具体建造者

 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
package main

// 普通房子建造者
type NormalBuilder struct {
    windowType string
    doorType   string
    floor      int
}

func newNormalBuilder() *NormalBuilder { return &NormalBuilder{} }
func (b *NormalBuilder) setWindowType() { b.windowType = "Wooden Window" }
func (b *NormalBuilder) setDoorType()   { b.doorType = "Wooden Door" }
func (b *NormalBuilder) setNumFloor()   { b.floor = 2 }
func (b *NormalBuilder) getHouse() House {
    return House{doorType: b.doorType, windowType: b.windowType, floor: b.floor}
}

// 冰屋建造者
type IglooBuilder struct {
    windowType string
    doorType   string
    floor      int
}

func newIglooBuilder() *IglooBuilder { return &IglooBuilder{} }
func (b *IglooBuilder) setWindowType() { b.windowType = "Snow Window" }
func (b *IglooBuilder) setDoorType()   { b.doorType = "Snow Door" }
func (b *IglooBuilder) setNumFloor()   { b.floor = 1 }
func (b *IglooBuilder) getHouse() House {
    return House{doorType: b.doorType, windowType: b.windowType, floor: b.floor}
}

house.go - 产品

1
2
3
4
5
6
7
package main

type House struct {
    windowType string
    doorType   string
    floor      int
}

director.go - 指挥者

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package main

type Director struct {
    builder IBuilder
}

func newDirector(b IBuilder) *Director {
    return &Director{builder: b}
}

func (d *Director) setBuilder(b IBuilder) {
    d.builder = b
}

func (d *Director) buildHouse() House {
    d.builder.setDoorType()
    d.builder.setWindowType()
    d.builder.setNumFloor()
    return d.builder.getHouse()
}

main.go

 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
package main

import "fmt"

func main() {
    normalBuilder := getBuilder("normal")
    iglooBuilder := getBuilder("igloo")

    director := newDirector(normalBuilder)
    normalHouse := director.buildHouse()
    fmt.Printf("Normal House Door Type: %s
", normalHouse.doorType)
    fmt.Printf("Normal House Window Type: %s
", normalHouse.windowType)
    fmt.Printf("Normal House Num Floor: %d
", normalHouse.floor)

    director.setBuilder(iglooBuilder)
    iglooHouse := director.buildHouse()
    fmt.Printf("
Igloo House Door Type: %s
", iglooHouse.doorType)
    fmt.Printf("Igloo House Window Type: %s
", iglooHouse.windowType)
    fmt.Printf("Igloo House Num Floor: %d
", iglooHouse.floor)
}

输出:

1
2
3
4
5
6
7
Normal House Door Type: Wooden Door
Normal House Window Type: Wooden Window
Normal House Num Floor: 2

Igloo House Door Type: Snow Door
Igloo House Window Type: Snow Window
Igloo House Num Floor: 1

5. 原型模式 (Prototype)

意图: 允许复制现有对象,而无需使代码依赖于它们的类。

示例: 操作系统文件系统的递归克隆。文件夹包含文件和文件夹,可以无限嵌套。

inode.go - 原型接口

1
2
3
4
5
6
package main

type Inode interface {
    print(string)
    clone() Inode
}

file.go - 具体原型(文件)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import "fmt"

type File struct {
    name string
}

func (f *File) print(indentation string) {
    fmt.Println(indentation + f.name)
}

func (f *File) clone() Inode {
    return &File{name: f.name + "_clone"}
}

folder.go - 具体原型(文件夹)

 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
package main

import "fmt"

type Folder struct {
    children []Inode
    name     string
}

func (f *Folder) print(indentation string) {
    fmt.Println(indentation + f.name)
    for _, i := range f.children {
        i.print(indentation + indentation)
    }
}

func (f *Folder) clone() Inode {
    cloneFolder := &Folder{name: f.name + "_clone"}
    var tempChildren []Inode
    for _, i := range f.children {
        copy := i.clone()
        tempChildren = append(tempChildren, copy)
    }
    cloneFolder.children = tempChildren
    return cloneFolder
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

func main() {
    file1 := &File{name: "File1"}
    file2 := &File{name: "File2"}
    file3 := &File{name: "File3"}

    folder1 := &Folder{children: []Inode{file1}, name: "Folder1"}
    folder2 := &Folder{children: []Inode{folder1, file2, file3}, name: "Folder2"}

    fmt.Println("
Printing hierarchy for Folder2")
    folder2.print("  ")

    cloneFolder := folder2.clone()
    fmt.Println("
Printing hierarchy for clone Folder")
    cloneFolder.print("  ")
}

输出:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Printing hierarchy for Folder2
  Folder2
    Folder1
        File1
    File2
    File3

Printing hierarchy for clone Folder
  Folder2_clone
    Folder1_clone
        File1_clone
    File2_clone
    File3_clone

结构型模式 (Structural Patterns)

6. 适配器模式 (Adapter)

意图: 允许接口不兼容的对象能够协同工作。

示例: 客户端期望 Lightning 接口,但有一个 Windows 笔记本电脑只提供 USB 接口。适配器将 Lightning 信号转换为 USB 信号。

computer.go - 客户端接口

1
2
3
4
5
package main

type Computer interface {
    InsertIntoLightningPort()
}

mac.go - 服务(兼容)

1
2
3
4
5
6
7
8
9
package main

import "fmt"

type Mac struct{}

func (m *Mac) InsertIntoLightningPort() {
    fmt.Println("Lightning connector is plugged into mac machine.")
}

windows.go - 不兼容的服务

1
2
3
4
5
6
7
8
9
package main

import "fmt"

type Windows struct{}

func (w *Windows) insertIntoUSBPort() {
    fmt.Println("USB connector is plugged into windows machine.")
}

windowsAdapter.go - 适配器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import "fmt"

type WindowsAdapter struct {
    windowMachine *Windows
}

func (w *WindowsAdapter) InsertIntoLightningPort() {
    fmt.Println("Adapter converts Lightning signal to USB.")
    w.windowMachine.insertIntoUSBPort()
}

client.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package main

import "fmt"

type Client struct{}

func (c *Client) InsertLightningConnectorIntoComputer(com Computer) {
    fmt.Println("Client inserts Lightning connector into computer.")
    com.InsertIntoLightningPort()
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package main

func main() {
    client := &Client{}
    mac := &Mac{}
    client.InsertLightningConnectorIntoComputer(mac)

    windowsMachine := &Windows{}
    windowsMachineAdapter := &WindowsAdapter{windowMachine: windowsMachine}
    client.InsertLightningConnectorIntoComputer(windowsMachineAdapter)
}

输出:

1
2
3
4
5
Client inserts Lightning connector into computer.
Lightning connector is plugged into mac machine.
Client inserts Lightning connector into computer.
Adapter converts Lightning signal to USB.
USB connector is plugged into windows machine.

7. 桥接模式 (Bridge)

意图: 将一个大类或一组紧密相关的类拆分为两个独立的类层次结构——抽象和实现——使它们可以独立开发。

示例: 两种计算机(Mac、Windows)和两种打印机(Epson、HP),需要在任意组合下工作。

computer.go / printer.go - 抽象与实现接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

// 抽象层次:计算机
type Computer interface {
    Print()
    SetPrinter(Printer)
}

// 实现层次:打印机
type Printer interface {
    PrintFile()
}

mac.go / windows.go - 精炼抽象

 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
package main

import "fmt"

// Mac
type Mac struct {
    printer Printer
}

func (m *Mac) Print() {
    fmt.Println("Print request for mac")
    m.printer.PrintFile()
}

func (m *Mac) SetPrinter(p Printer) {
    m.printer = p
}

// Windows
type Windows struct {
    printer Printer
}

func (w *Windows) Print() {
    fmt.Println("Print request for windows")
    w.printer.PrintFile()
}

func (w *Windows) SetPrinter(p Printer) {
    w.printer = p
}

epson.go / hp.go - 具体实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import "fmt"

// Epson
type Epson struct{}

func (p *Epson) PrintFile() {
    fmt.Println("Printing by a EPSON Printer")
}

// HP
type Hp struct{}

func (p *Hp) PrintFile() {
    fmt.Println("Printing by a HP Printer")
}

main.go

 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
package main

import "fmt"

func main() {
    hpPrinter := &Hp{}
    epsonPrinter := &Epson{}

    macComputer := &Mac{}
    macComputer.SetPrinter(hpPrinter)
    macComputer.Print()
    fmt.Println()

    macComputer.SetPrinter(epsonPrinter)
    macComputer.Print()
    fmt.Println()

    winComputer := &Windows{}
    winComputer.SetPrinter(hpPrinter)
    winComputer.Print()
    fmt.Println()

    winComputer.SetPrinter(epsonPrinter)
    winComputer.Print()
    fmt.Println()
}

8. 组合模式 (Composite)

意图: 允许你将对象组合成树结构,然后像处理单个对象一样处理这些结构。

示例: 文件系统中的文件和文件夹。对文件夹的搜索操作会递归应用到所有子文件和子文件夹。

component.go - 组件接口

1
2
3
4
5
package main

type Component interface {
    search(string)
}

folder.go - 组合(容器)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

type Folder struct {
    components []Component
    name       string
}

func (f *Folder) search(keyword string) {
    fmt.Printf("Serching recursively for keyword %s in folder %s
", keyword, f.name)
    for _, composite := range f.components {
        composite.search(keyword)
    }
}

func (f *Folder) add(c Component) {
    f.components = append(f.components, c)
}

file.go - 叶子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import "fmt"

type File struct {
    name string
}

func (f *File) search(keyword string) {
    fmt.Printf("Searching for keyword %s in file %s
", keyword, f.name)
}

func (f *File) getName() string {
    return f.name
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

func main() {
    file1 := &File{name: "File1"}
    file2 := &File{name: "File2"}
    file3 := &File{name: "File3"}

    folder1 := &Folder{name: "Folder1"}
    folder1.add(file1)

    folder2 := &Folder{name: "Folder2"}
    folder2.add(file2)
    folder2.add(file3)
    folder2.add(folder1)

    folder2.search("rose")
}

输出:

1
2
3
4
5
Serching recursively for keyword rose in folder Folder2
Searching for keyword rose in file File2
Searching for keyword rose in file File3
Serching recursively for keyword rose in folder Folder1
Searching for keyword rose in file File1

9. 装饰器模式 (Decorator)

意图: 允许你通过将对象放入包含行为的特殊包装器对象中来动态地为对象添加新行为。

示例: 披萨可以添加不同的配料(芝士、番茄),每个配料都会增加价格。

pizza.go - 组件接口

1
2
3
4
5
package main

type IPizza interface {
    getPrice() int
}

veggieMania.go - 具体组件

1
2
3
4
5
6
7
package main

type VeggieMania struct{}

func (p *VeggieMania) getPrice() int {
    return 15
}

tomatoTopping.go / cheeseTopping.go - 具体装饰器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package main

// 番茄配料
type TomatoTopping struct {
    pizza IPizza
}

func (c *TomatoTopping) getPrice() int {
    pizzaPrice := c.pizza.getPrice()
    return pizzaPrice + 7
}

// 芝士配料
type CheeseTopping struct {
    pizza IPizza
}

func (c *CheeseTopping) getPrice() int {
    pizzaPrice := c.pizza.getPrice()
    return pizzaPrice + 10
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import "fmt"

func main() {
    pizza := &VeggieMania{}

    // 添加芝士配料
    pizzaWithCheese := &CheeseTopping{pizza: pizza}

    // 添加番茄配料
    pizzaWithCheeseAndTomato := &TomatoTopping{pizza: pizzaWithCheese}

    fmt.Printf("Price of veggeMania with tomato and cheese topping is %d
",
        pizzaWithCheeseAndTomato.getPrice())
}

输出:

1
Price of veggeMania with tomato and cheese topping is 32

10. 外观模式 (Facade)

意图: 为复杂的类、库或框架系统提供简化的(但有限的)接口。

示例: 使用信用卡购买披萨涉及多个子系统(账户验证、安全码验证、余额扣款、记账、发送通知)。外观模式将这些复杂性隐藏在一个简单接口后面。

walletFacade.go - 外观

 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
package main

import "fmt"

type WalletFacade struct {
    account      *Account
    wallet       *Wallet
    securityCode *SecurityCode
    notification *Notification
    ledger       *Ledger
}

func newWalletFacade(accountID string, code int) *WalletFacade {
    fmt.Println("Starting create account")
    walletFacade := &WalletFacade{
        account:      newAccount(accountID),
        securityCode: newSecurityCode(code),
        wallet:       newWallet(),
        notification: &Notification{},
        ledger:       &Ledger{},
    }
    fmt.Println("Account created")
    return walletFacade
}

func (w *WalletFacade) addMoneyToWallet(accountID string, securityCode int, amount int) error {
    fmt.Println("Starting add money to wallet")
    if err := w.account.checkAccount(accountID); err != nil {
        return err
    }
    if err := w.securityCode.checkCode(securityCode); err != nil {
        return err
    }
    w.wallet.creditBalance(amount)
    w.notification.sendWalletCreditNotification()
    w.ledger.makeEntry(accountID, "credit", amount)
    return nil
}

func (w *WalletFacade) deductMoneyFromWallet(accountID string, securityCode int, amount int) error {
    fmt.Println("Starting debit money from wallet")
    if err := w.account.checkAccount(accountID); err != nil {
        return err
    }
    if err := w.securityCode.checkCode(securityCode); err != nil {
        return err
    }
    if err := w.wallet.debitBalance(amount); err != nil {
        return err
    }
    w.notification.sendWalletDebitNotification()
    w.ledger.makeEntry(accountID, "debit", amount)
    return nil
}

子系统

 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
package main

import "fmt"

// 账户
type Account struct {
    name string
}

func newAccount(accountName string) *Account {
    return &Account{name: accountName}
}

func (a *Account) checkAccount(accountName string) error {
    if a.name != accountName {
        return fmt.Errorf("Account Name is incorrect")
    }
    fmt.Println("Account Verified")
    return nil
}

// 安全码
type SecurityCode struct {
    code int
}

func newSecurityCode(code int) *SecurityCode {
    return &SecurityCode{code: code}
}

func (s *SecurityCode) checkCode(incomingCode int) error {
    if s.code != incomingCode {
        return fmt.Errorf("Security Code is incorrect")
    }
    fmt.Println("SecurityCode Verified")
    return nil
}

// 钱包
type Wallet struct {
    balance int
}

func newWallet() *Wallet {
    return &Wallet{balance: 0}
}

func (w *Wallet) creditBalance(amount int) {
    w.balance += amount
    fmt.Println("Wallet balance added successfully")
}

func (w *Wallet) debitBalance(amount int) error {
    if w.balance < amount {
        return fmt.Errorf("Balance is not sufficient")
    }
    fmt.Println("Wallet balance is Sufficient")
    w.balance = w.balance - amount
    return nil
}

// 账本
type Ledger struct{}

func (s *Ledger) makeEntry(accountID, txnType string, amount int) {
    fmt.Printf("Make ledger entry for accountId %s with txnType %s for amount %d
",
        accountID, txnType, amount)
}

// 通知
type Notification struct{}

func (n *Notification) sendWalletCreditNotification() {
    fmt.Println("Sending wallet credit notification")
}

func (n *Notification) sendWalletDebitNotification() {
    fmt.Println("Sending wallet debit notification")
}

main.go

 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
package main

import (
    "fmt"
    "log"
)

func main() {
    fmt.Println()
    walletFacade := newWalletFacade("abc", 1234)
    fmt.Println()

    err := walletFacade.addMoneyToWallet("abc", 1234, 10)
    if err != nil {
        log.Fatalf("Error: %s
", err.Error())
    }

    fmt.Println()
    err = walletFacade.deductMoneyFromWallet("abc", 1234, 5)
    if err != nil {
        log.Fatalf("Error: %s
", err.Error())
    }
}

输出:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Starting create account
Account created

Starting add money to wallet
Account Verified
SecurityCode Verified
Wallet balance added successfully
Sending wallet credit notification
Make ledger entry for accountId abc with txnType credit for amount 10

Starting debit money from wallet
Account Verified
SecurityCode Verified
Wallet balance is Sufficient
Sending wallet debit notification
Make ledger entry for accountId abc with txnType debit for amount 5

11. 享元模式 (Flyweight)

意图: 允许程序通过在不同对象之间共享公共状态部分来支持大量对象,从而将内存消耗保持在较低水平。

示例: CS 游戏中,恐怖分子和反恐精英有不同的服装。5 个恐怖分子共享一套服装,5 个反恐精英共享另一套服装,而不是每人创建一套。

关键概念:

  • 内部状态 (Intrinsic State): 服装,可以被多个对象共享
  • 外部状态 (Extrinsic State): 玩家位置和武器,每个对象不同

dress.go - 享元接口

1
2
3
4
5
package main

type Dress interface {
    getColor() string
}

terroristDress.go / counterTerroristDress.go - 具体享元

 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
package main

// 恐怖分子服装
type TerroristDress struct {
    color string
}

func (t *TerroristDress) getColor() string {
    return t.color
}

func newTerroristDress() *TerroristDress {
    return &TerroristDress{color: "red"}
}

// 反恐精英服装
type CounterTerroristDress struct {
    color string
}

func (c *CounterTerroristDress) getColor() string {
    return c.color
}

func newCounterTerroristDress() *CounterTerroristDress {
    return &CounterTerroristDress{color: "green"}
}

dressFactory.go - 享元工厂

 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
package main

import "fmt"

const (
    TerroristDressType         = "tDress"
    CounterTerrroristDressType = "ctDress"
)

var (
    dressFactorySingleInstance = &DressFactory{
        dressMap: make(map[string]Dress),
    }
)

type DressFactory struct {
    dressMap map[string]Dress
}

func (d *DressFactory) getDressByType(dressType string) (Dress, error) {
    if d.dressMap[dressType] != nil {
        return d.dressMap[dressType], nil
    }
    if dressType == TerroristDressType {
        d.dressMap[dressType] = newTerroristDress()
        return d.dressMap[dressType], nil
    }
    if dressType == CounterTerrroristDressType {
        d.dressMap[dressType] = newCounterTerroristDress()
        return d.dressMap[dressType], nil
    }
    return nil, fmt.Errorf("Wrong dress type passed")
}

func getDressFactorySingleInstance() *DressFactory {
    return dressFactorySingleInstance
}

player.go - 上下文

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package main

type Player struct {
    dress      Dress
    playerType string
    lat        int
    long       int
}

func newPlayer(playerType, dressType string) *Player {
    dress, _ := getDressFactorySingleInstance().getDressByType(dressType)
    return &Player{
        playerType: playerType,
        dress:      dress,
    }
}

func (p *Player) newLocation(lat, long int) {
    p.lat = lat
    p.long = long
}

game.go / main.go

 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 main

import "fmt"

type game struct {
    terrorists        []*Player
    counterTerrorists []*Player
}

func newGame() *game {
    return &game{
        terrorists:        make([]*Player, 1),
        counterTerrorists: make([]*Player, 1),
    }
}

func (c *game) addTerrorist(dressType string) {
    player := newPlayer("T", dressType)
    c.terrorists = append(c.terrorists, player)
}

func (c *game) addCounterTerrorist(dressType string) {
    player := newPlayer("CT", dressType)
    c.counterTerrorists = append(c.counterTerrorists, player)
}

func main() {
    game := newGame()

    game.addTerrorist(TerroristDressType)
    game.addTerrorist(TerroristDressType)
    game.addTerrorist(TerroristDressType)
    game.addTerrorist(TerroristDressType)

    game.addCounterTerrorist(CounterTerrroristDressType)
    game.addCounterTerrorist(CounterTerrroristDressType)
    game.addCounterTerrorist(CounterTerrroristDressType)

    dressFactoryInstance := getDressFactorySingleInstance()
    for dressType, dress := range dressFactoryInstance.dressMap {
        fmt.Printf("DressColorType: %s
DressColor: %s
", dressType, dress.getColor())
    }
}

输出:

1
2
3
4
DressColorType: ctDress
DressColor: green
DressColorType: tDress
DressColor: red

12. 代理模式 (Proxy)

意图: 提供一个对象作为真实服务对象的替代品。代理接收客户端请求,做一些工作(访问控制、缓存等),然后将请求传递给服务对象。

示例: Nginx 作为应用服务器的代理,提供受控访问、速率限制和请求缓存。

server.go - 主题接口

1
2
3
4
5
package main

type server interface {
    handleRequest(string, string) (int, string)
}

application.go - 真实主题

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

type Application struct{}

func (a *Application) handleRequest(url, method string) (int, string) {
    if url == "/app/status" && method == "GET" {
        return 200, "Ok"
    }
    if url == "/create/user" && method == "POST" {
        return 201, "User Created"
    }
    return 404, "Not Ok"
}

nginx.go - 代理

 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
package main

type Nginx struct {
    application       *Application
    maxAllowedRequest int
    rateLimiter       map[string]int
}

func newNginxServer() *Nginx {
    return &Nginx{
        application:       &Application{},
        maxAllowedRequest: 2,
        rateLimiter:       make(map[string]int),
    }
}

func (n *Nginx) handleRequest(url, method string) (int, string) {
    allowed := n.checkRateLimiting(url)
    if !allowed {
        return 403, "Not Allowed"
    }
    return n.application.handleRequest(url, method)
}

func (n *Nginx) checkRateLimiting(url string) bool {
    if n.rateLimiter[url] == 0 {
        n.rateLimiter[url] = 1
    }
    if n.rateLimiter[url] > n.maxAllowedRequest {
        return false
    }
    n.rateLimiter[url] = n.rateLimiter[url] + 1
    return true
}

main.go

 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
package main

import "fmt"

func main() {
    nginxServer := newNginxServer()
    appStatusURL := "/app/status"
    createuserURL := "/create/user"

    httpCode, body := nginxServer.handleRequest(appStatusURL, "GET")
    fmt.Printf("
Url: %s
HttpCode: %d
Body: %s
", appStatusURL, httpCode, body)

    httpCode, body = nginxServer.handleRequest(appStatusURL, "GET")
    fmt.Printf("
Url: %s
HttpCode: %d
Body: %s
", appStatusURL, httpCode, body)

    httpCode, body = nginxServer.handleRequest(appStatusURL, "GET")
    fmt.Printf("
Url: %s
HttpCode: %d
Body: %s
", appStatusURL, httpCode, body)

    httpCode, body = nginxServer.handleRequest(createuserURL, "POST")
    fmt.Printf("
Url: %s
HttpCode: %d
Body: %s
", createuserURL, httpCode, body)

    httpCode, body = nginxServer.handleRequest(createuserURL, "GET")
    fmt.Printf("
Url: %s
HttpCode: %d
Body: %s
", createuserURL, httpCode, body)
}

输出:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
Url: /app/status
HttpCode: 200
Body: Ok

Url: /app/status
HttpCode: 200
Body: Ok

Url: /app/status
HttpCode: 403
Body: Not Allowed

Url: /create/user
HttpCode: 201
Body: User Created

Url: /create/user
HttpCode: 404
Body: Not Ok

行为型模式 (Behavioral Patterns)

13. 责任链模式 (Chain of Responsibility)

意图: 允许将请求沿着处理者链传递,直到其中一个处理请求。

示例: 医院应用程序中,患者依次经过接待处、医生、药房、收银台。

department.go - 处理者接口

1
2
3
4
5
6
package main

type Department interface {
    execute(*Patient)
    setNext(Department)
}

reception.go / doctor.go / medical.go / cashier.go - 具体处理者

 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
package main

import "fmt"

// 接待处
type Reception struct {
    next Department
}

func (r *Reception) execute(p *Patient) {
    if p.registrationDone {
        fmt.Println("Patient registration already done")
        r.next.execute(p)
        return
    }
    fmt.Println("Reception registering patient")
    p.registrationDone = true
    r.next.execute(p)
}

func (r *Reception) setNext(next Department) {
    r.next = next
}

// 医生
type Doctor struct {
    next Department
}

func (d *Doctor) execute(p *Patient) {
    if p.doctorCheckUpDone {
        fmt.Println("Doctor checkup already done")
        d.next.execute(p)
        return
    }
    fmt.Println("Doctor checking patient")
    p.doctorCheckUpDone = true
    d.next.execute(p)
}

func (d *Doctor) setNext(next Department) {
    d.next = next
}

// 药房
type Medical struct {
    next Department
}

func (m *Medical) execute(p *Patient) {
    if p.medicineDone {
        fmt.Println("Medicine already given to patient")
        m.next.execute(p)
        return
    }
    fmt.Println("Medical giving medicine to patient")
    p.medicineDone = true
    m.next.execute(p)
}

func (m *Medical) setNext(next Department) {
    m.next = next
}

// 收银台
type Cashier struct {
    next Department
}

func (c *Cashier) execute(p *Patient) {
    if p.paymentDone {
        fmt.Println("Payment Done")
    }
    fmt.Println("Cashier getting money from patient patient")
}

func (c *Cashier) setNext(next Department) {
    c.next = next
}

patient.go

1
2
3
4
5
6
7
8
9
package main

type Patient struct {
    name              string
    registrationDone  bool
    doctorCheckUpDone bool
    medicineDone      bool
    paymentDone       bool
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

func main() {
    cashier := &Cashier{}

    medical := &Medical{}
    medical.setNext(cashier)

    doctor := &Doctor{}
    doctor.setNext(medical)

    reception := &Reception{}
    reception.setNext(doctor)

    patient := &Patient{name: "abc"}
    reception.execute(patient)
}

输出:

1
2
3
4
Reception registering patient
Doctor checking patient
Medical giving medicine to patient
Cashier getting money from patient patient

14. 命令模式 (Command)

意图: 将请求或简单操作转换为对象。转换允许延迟或远程执行命令、存储命令历史等。

示例: 电视可以通过遥控器或电视上的按钮打开。两者都嵌入相同的 ON 命令对象。

command.go - 命令接口

1
2
3
4
5
package main

type Command interface {
    execute()
}

device.go - 接收者接口

1
2
3
4
5
6
package main

type Device interface {
    on()
    off()
}

tv.go - 具体接收者

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import "fmt"

type Tv struct {
    isRunning bool
}

func (t *Tv) on() {
    t.isRunning = true
    fmt.Println("Turning tv on")
}

func (t *Tv) off() {
    t.isRunning = false
    fmt.Println("Turning tv off")
}

onCommand.go / offCommand.go - 具体命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

// 开机命令
type OnCommand struct {
    device Device
}

func (c *OnCommand) execute() {
    c.device.on()
}

// 关机命令
type OffCommand struct {
    device Device
}

func (c *OffCommand) execute() {
    c.device.off()
}

button.go - 调用者

1
2
3
4
5
6
7
8
9
package main

type Button struct {
    command Command
}

func (b *Button) press() {
    b.command.execute()
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

func main() {
    tv := &Tv{}

    onCommand := &OnCommand{device: tv}
    offCommand := &OffCommand{device: tv}

    onButton := &Button{command: onCommand}
    onButton.press()

    offButton := &Button{command: offCommand}
    offButton.press()
}

输出:

1
2
Turning tv on
Turning tv off

15. 迭代器模式 (Iterator)

意图: 允许顺序遍历复杂数据结构,而不暴露其内部细节。

示例: 遍历用户集合。

collection.go / iterator.go - 集合与迭代器接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

// 集合接口
type Collection interface {
    createIterator() Iterator
}

// 迭代器接口
type Iterator interface {
    hasNext() bool
    getNext() *User
}

userCollection.go / userIterator.go - 具体实现

 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
package main

// 用户集合
type UserCollection struct {
    users []*User
}

func (u *UserCollection) createIterator() Iterator {
    return &UserIterator{users: u.users}
}

// 用户迭代器
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
}

user.go

1
2
3
4
5
6
package main

type User struct {
    name string
    age  int
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import "fmt"

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
", user)
    }
}

输出:

1
2
User is &{name:a age:30}
User is &{name:b age:20}

16. 中介者模式 (Mediator)

意图: 通过使组件通过特殊的中介者对象间接通信,减少程序组件之间的耦合。

示例: 火车站交通系统。列车之间不直接通信,而是通过 stationManager(中介者)协调站台使用。

train.go / mediator.go - 组件与中介者接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

// 列车接口
type Train interface {
    arrive()
    depart()
    permitArrival()
}

// 中介者接口
type Mediator interface {
    canArrive(Train) bool
    notifyAboutDeparture()
}

passengerTrain.go / freightTrain.go - 具体组件

 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
package main

import "fmt"

// 客运列车
type PassengerTrain struct {
    mediator Mediator
}

func (g *PassengerTrain) arrive() {
    if !g.mediator.canArrive(g) {
        fmt.Println("PassengerTrain: Arrival blocked, waiting")
        return
    }
    fmt.Println("PassengerTrain: Arrived")
}

func (g *PassengerTrain) depart() {
    fmt.Println("PassengerTrain: Leaving")
    g.mediator.notifyAboutDeparture()
}

func (g *PassengerTrain) permitArrival() {
    fmt.Println("PassengerTrain: Arrival permitted, arriving")
    g.arrive()
}

// 货运列车
type FreightTrain struct {
    mediator Mediator
}

func (g *FreightTrain) arrive() {
    if !g.mediator.canArrive(g) {
        fmt.Println("FreightTrain: Arrival blocked, waiting")
        return
    }
    fmt.Println("FreightTrain: Arrived")
}

func (g *FreightTrain) depart() {
    fmt.Println("FreightTrain: Leaving")
    g.mediator.notifyAboutDeparture()
}

func (g *FreightTrain) permitArrival() {
    fmt.Println("FreightTrain: Arrival permitted")
    g.arrive()
}

stationManager.go - 具体中介者

 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
package main

type StationManager struct {
    isPlatformFree bool
    trainQueue     []Train
}

func newStationManger() *StationManager {
    return &StationManager{isPlatformFree: true}
}

func (s *StationManager) canArrive(t Train) bool {
    if s.isPlatformFree {
        s.isPlatformFree = false
        return true
    }
    s.trainQueue = append(s.trainQueue, t)
    return false
}

func (s *StationManager) notifyAboutDeparture() {
    if !s.isPlatformFree {
        s.isPlatformFree = true
    }
    if len(s.trainQueue) > 0 {
        firstTrainInQueue := s.trainQueue[0]
        s.trainQueue = s.trainQueue[1:]
        firstTrainInQueue.permitArrival()
    }
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

func main() {
    stationManager := newStationManger()

    passengerTrain := &PassengerTrain{mediator: stationManager}
    freightTrain := &FreightTrain{mediator: stationManager}

    passengerTrain.arrive()
    freightTrain.arrive()
    passengerTrain.depart()
}

输出:

1
2
3
4
5
PassengerTrain: Arrived
FreightTrain: Arrival blocked, waiting
PassengerTrain: Leaving
FreightTrain: Arrival permitted
FreightTrain: Arrived

17. 备忘录模式 (Memento)

意图: 允许在不暴露对象实现细节的情况下捕获和恢复对象的内部状态。

示例: 实现对象的撤销-重做操作。

originator.go - 原发器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package main

type Originator struct {
    state string
}

func (e *Originator) createMemento() *Memento {
    return &Memento{state: e.state}
}

func (e *Originator) restoreMemento(m *Memento) {
    e.state = m.getSavedState()
}

func (e *Originator) setState(state string) {
    e.state = state
}

func (e *Originator) getState() string {
    return e.state
}

memento.go - 备忘录

1
2
3
4
5
6
7
8
9
package main

type Memento struct {
    state string
}

func (m *Memento) getSavedState() string {
    return m.state
}

caretaker.go - 负责人

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

type Caretaker struct {
    mementoArray []*Memento
}

func (c *Caretaker) addMemento(m *Memento) {
    c.mementoArray = append(c.mementoArray, m)
}

func (c *Caretaker) getMemento(index int) *Memento {
    return c.mementoArray[index]
}

main.go

 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
package main

import "fmt"

func main() {
    caretaker := &Caretaker{mementoArray: make([]*Memento, 0)}
    originator := &Originator{state: "A"}

    fmt.Printf("Originator Current State: %s
", originator.getState())
    caretaker.addMemento(originator.createMemento())

    originator.setState("B")
    fmt.Printf("Originator Current State: %s
", originator.getState())
    caretaker.addMemento(originator.createMemento())

    originator.setState("C")
    fmt.Printf("Originator Current State: %s
", originator.getState())
    caretaker.addMemento(originator.createMemento())

    originator.restoreMemento(caretaker.getMemento(1))
    fmt.Printf("Restored to State: %s
", originator.getState())

    originator.restoreMemento(caretaker.getMemento(0))
    fmt.Printf("Restored to State: %s
", originator.getState())
}

输出:

1
2
3
4
5
Originator Current State: A
Originator Current State: B
Originator Current State: C
Restored to State: B
Restored to State: A

18. 观察者模式 (Observer)

意图: 允许你定义一种订阅机制,在对象事件发生时通知多个"观察"该对象的其他对象。

示例: 电商网站中,商品缺货时,订阅该商品的客户会收到通知。

subject.go / observer.go - 主题与观察者接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

// 主题接口
type Subject interface {
    register(observer Observer)
    deregister(observer Observer)
    notifyAll()
}

// 观察者接口
type Observer interface {
    update(string)
    getID() string
}

item.go - 具体主题

 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 main

import "fmt"

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
", 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
}

customer.go - 具体观察者

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import "fmt"

type Customer struct {
    id string
}

func (c *Customer) update(itemName string) {
    fmt.Printf("Sending email to customer %s for item %s
", c.id, itemName)
}

func (c *Customer) getID() string {
    return c.id
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

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()
}

输出:

1
2
3
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

19. 状态模式 (State)

意图: 允许对象在其内部状态改变时改变其行为。

示例: 自动售货机有四种状态:有商品、无商品、请求商品、已投币。根据当前状态,机器对相同请求的行为不同。

state.go - 状态接口

1
2
3
4
5
6
7
8
package main

type State interface {
    addItem(int) error
    requestItem() error
    insertMoney(money int) error
    dispenseItem() error
}

vendingMachine.go - 上下文

 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
package main

import "fmt"

type VendingMachine struct {
    hasItem       State
    itemRequested State
    hasMoney      State
    noItem        State
    currentState  State
    itemCount     int
    itemPrice     int
}

func newVendingMachine(itemCount, itemPrice int) *VendingMachine {
    v := &VendingMachine{itemCount: itemCount, itemPrice: itemPrice}
    hasItemState := &HasItemState{vendingMachine: v}
    itemRequestedState := &ItemRequestedState{vendingMachine: v}
    hasMoneyState := &HasMoneyState{vendingMachine: v}
    noItemState := &NoItemState{vendingMachine: v}

    v.setState(hasItemState)
    v.hasItem = hasItemState
    v.itemRequested = itemRequestedState
    v.hasMoney = hasMoneyState
    v.noItem = noItemState
    return v
}

func (v *VendingMachine) requestItem() error   { return v.currentState.requestItem() }
func (v *VendingMachine) addItem(count int) error { return v.currentState.addItem(count) }
func (v *VendingMachine) insertMoney(money int) error { return v.currentState.insertMoney(money) }
func (v *VendingMachine) dispenseItem() error  { return v.currentState.dispenseItem() }
func (v *VendingMachine) setState(s State)     { v.currentState = s }

func (v *VendingMachine) incrementItemCount(count int) {
    fmt.Printf("Adding %d items
", count)
    v.itemCount = v.itemCount + count
}

具体状态

 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
88
89
90
91
92
93
94
95
96
97
98
99
package main

import "fmt"

// 无商品状态
type NoItemState struct {
    vendingMachine *VendingMachine
}

func (i *NoItemState) requestItem() error {
    return fmt.Errorf("Item out of stock")
}
func (i *NoItemState) addItem(count int) error {
    i.vendingMachine.incrementItemCount(count)
    i.vendingMachine.setState(i.vendingMachine.hasItem)
    return nil
}
func (i *NoItemState) insertMoney(money int) error {
    return fmt.Errorf("Item out of stock")
}
func (i *NoItemState) dispenseItem() error {
    return fmt.Errorf("Item out of stock")
}

// 有商品状态
type HasItemState struct {
    vendingMachine *VendingMachine
}

func (i *HasItemState) requestItem() error {
    if i.vendingMachine.itemCount == 0 {
        i.vendingMachine.setState(i.vendingMachine.noItem)
        return fmt.Errorf("No item present")
    }
    fmt.Printf("Item requestd
")
    i.vendingMachine.setState(i.vendingMachine.itemRequested)
    return nil
}
func (i *HasItemState) addItem(count int) error {
    fmt.Printf("%d items added
", count)
    i.vendingMachine.incrementItemCount(count)
    return nil
}
func (i *HasItemState) insertMoney(money int) error {
    return fmt.Errorf("Please select item first")
}
func (i *HasItemState) dispenseItem() error {
    return fmt.Errorf("Please select item first")
}

// 请求商品状态
type ItemRequestedState struct {
    vendingMachine *VendingMachine
}

func (i *ItemRequestedState) requestItem() error {
    return fmt.Errorf("Item already requested")
}
func (i *ItemRequestedState) addItem(count int) error {
    return fmt.Errorf("Item Dispense in progress")
}
func (i *ItemRequestedState) insertMoney(money int) error {
    if money < i.vendingMachine.itemPrice {
        return fmt.Errorf("Inserted money is less. Please insert %d", i.vendingMachine.itemPrice)
    }
    fmt.Println("Money entered is ok")
    i.vendingMachine.setState(i.vendingMachine.hasMoney)
    return nil
}
func (i *ItemRequestedState) dispenseItem() error {
    return fmt.Errorf("Please insert money first")
}

// 已投币状态
type HasMoneyState struct {
    vendingMachine *VendingMachine
}

func (i *HasMoneyState) requestItem() error {
    return fmt.Errorf("Item dispense in progress")
}
func (i *HasMoneyState) addItem(count int) error {
    return fmt.Errorf("Item dispense in progress")
}
func (i *HasMoneyState) insertMoney(money int) error {
    return fmt.Errorf("Item out of stock")
}
func (i *HasMoneyState) dispenseItem() error {
    fmt.Println("Dispensing Item")
    i.vendingMachine.itemCount = i.vendingMachine.itemCount - 1
    if i.vendingMachine.itemCount == 0 {
        i.vendingMachine.setState(i.vendingMachine.noItem)
    } else {
        i.vendingMachine.setState(i.vendingMachine.hasItem)
    }
    return nil
}

main.go

 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
package main

import (
    "fmt"
    "log"
)

func main() {
    vendingMachine := newVendingMachine(1, 10)

    err := vendingMachine.requestItem()
    if err != nil {
        log.Fatalf(err.Error())
    }

    err = vendingMachine.insertMoney(10)
    if err != nil {
        log.Fatalf(err.Error())
    }

    err = vendingMachine.dispenseItem()
    if err != nil {
        log.Fatalf(err.Error())
    }

    fmt.Println()

    err = vendingMachine.addItem(2)
    if err != nil {
        log.Fatalf(err.Error())
    }

    fmt.Println()

    err = vendingMachine.requestItem()
    if err != nil {
        log.Fatalf(err.Error())
    }

    err = vendingMachine.insertMoney(10)
    if err != nil {
        log.Fatalf(err.Error())
    }

    err = vendingMachine.dispenseItem()
    if err != nil {
        log.Fatalf(err.Error())
    }
}

输出:

1
2
3
4
5
6
7
8
9
Item requestd
Money entered is ok
Dispensing Item

Adding 2 items

Item requestd
Money entered is ok
Dispensing Item

20. 策略模式 (Strategy)

意图: 允许你定义一系列算法,将每个算法放入单独的类中,并使它们可以互换。

示例: 内存缓存达到最大大小时,可以通过多种算法(LRU、FIFO、LFU)驱逐条目。策略模式将缓存类与这些算法解耦。

evictionAlgo.go - 策略接口

1
2
3
4
5
package main

type EvictionAlgo interface {
    evict(c *Cache)
}

fifo.go / lru.go / lfu.go - 具体策略

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

// FIFO 策略
type Fifo struct{}

func (l *Fifo) evict(c *Cache) {
    fmt.Println("Evicting by fifo strtegy")
}

// LRU 策略
type Lru struct{}

func (l *Lru) evict(c *Cache) {
    fmt.Println("Evicting by lru strtegy")
}

// LFU 策略
type Lfu struct{}

func (l *Lfu) evict(c *Cache) {
    fmt.Println("Evicting by lfu strtegy")
}

cache.go - 上下文

 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
package main

type Cache struct {
    storage      map[string]string
    evictionAlgo EvictionAlgo
    capacity     int
    maxCapacity  int
}

func initCache(e EvictionAlgo) *Cache {
    storage := make(map[string]string)
    return &Cache{
        storage:      storage,
        evictionAlgo: e,
        capacity:     0,
        maxCapacity:  2,
    }
}

func (c *Cache) setEvictionAlgo(e EvictionAlgo) {
    c.evictionAlgo = e
}

func (c *Cache) add(key, value string) {
    if c.capacity == c.maxCapacity {
        c.evict()
    }
    c.capacity++
    c.storage[key] = value
}

func (c *Cache) get(key string) {
    delete(c.storage, key)
}

func (c *Cache) evict() {
    c.evictionAlgo.evict(c)
    c.capacity--
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

func main() {
    lfu := &Lfu{}
    cache := initCache(lfu)

    cache.add("a", "1")
    cache.add("b", "2")
    cache.add("c", "3")

    lru := &Lru{}
    cache.setEvictionAlgo(lru)
    cache.add("d", "4")

    fifo := &Fifo{}
    cache.setEvictionAlgo(fifo)
    cache.add("e", "5")
}

输出:

1
2
3
Evicting by lfu strtegy
Evicting by lru strtegy
Evicting by fifo strtegy

21. 模板方法模式 (Template Method)

意图: 允许你在基类中定义算法的骨架,并让子类覆盖特定步骤而不改变算法的结构。

示例: 一次性密码(OTP)功能。无论是短信 OTP 还是邮件 OTP,整个 OTP 流程都相同:生成随机数字 -> 保存到缓存 -> 准备内容 -> 发送通知。

otp.go - 模板方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

type IOtp interface {
    genRandomOTP(int) string
    saveOTPCache(string)
    getMessage(string) string
    sendNotification(string) error
}

type Otp struct {
    iOtp IOtp
}

func (o *Otp) genAndSendOTP(otpLength int) error {
    otp := o.iOtp.genRandomOTP(otpLength)
    o.iOtp.saveOTPCache(otp)
    message := o.iOtp.getMessage(otp)
    err := o.iOtp.sendNotification(message)
    if err != nil {
        return err
    }
    return nil
}

sms.go / email.go - 具体实现

 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
package main

import "fmt"

// SMS OTP
type Sms struct {
    Otp
}

func (s *Sms) genRandomOTP(len int) string {
    randomOTP := "1234"
    fmt.Printf("SMS: generating random otp %s
", randomOTP)
    return randomOTP
}

func (s *Sms) saveOTPCache(otp string) {
    fmt.Printf("SMS: saving otp: %s to cache
", otp)
}

func (s *Sms) getMessage(otp string) string {
    return "SMS OTP for login is " + otp
}

func (s *Sms) sendNotification(message string) error {
    fmt.Printf("SMS: sending sms: %s
", message)
    return nil
}

// Email OTP
type Email struct {
    Otp
}

func (s *Email) genRandomOTP(len int) string {
    randomOTP := "1234"
    fmt.Printf("EMAIL: generating random otp %s
", randomOTP)
    return randomOTP
}

func (s *Email) saveOTPCache(otp string) {
    fmt.Printf("EMAIL: saving otp: %s to cache
", otp)
}

func (s *Email) getMessage(otp string) string {
    return "EMAIL OTP for login is " + otp
}

func (s *Email) sendNotification(message string) error {
    fmt.Printf("EMAIL: sending email: %s
", message)
    return nil
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

import "fmt"

func main() {
    smsOTP := &Sms{}
    o := Otp{iOtp: smsOTP}
    o.genAndSendOTP(4)

    fmt.Println("")
    emailOTP := &Email{}
    o = Otp{iOtp: emailOTP}
    o.genAndSendOTP(4)
}

输出:

1
2
3
4
5
6
7
SMS: generating random otp 1234
SMS: saving otp: 1234 to cache
SMS: sending sms: SMS OTP for login is 1234

EMAIL: generating random otp 1234
EMAIL: saving otp: 1234 to cache
EMAIL: sending email: EMAIL OTP for login is 1234

22. 访问者模式 (Visitor)

意图: 允许你在不修改现有类层次结构的情况下向其添加新行为。

示例: 维护一个包含不同形状(正方形、圆形、矩形)的图形库。需要添加 getArea 行为,但不想修改现有形状类。

Go 注意点: Go 不支持方法重载,因此访问者接口需要为每种形状定义不同的方法名(如 visitForSquare、visitForCircle)。

shape.go - 元素接口

1
2
3
4
5
6
package main

type Shape interface {
    getType() string
    accept(Visitor)
}

square.go / circle.go / rectangle.go - 具体元素

 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
package main

// 正方形
type Square struct {
    side int
}

func (s *Square) accept(v Visitor) {
    v.visitForSquare(s)
}

func (s *Square) getType() string {
    return "Square"
}

// 圆形
type Circle struct {
    radius int
}

func (c *Circle) accept(v Visitor) {
    v.visitForCircle(c)
}

func (c *Circle) getType() string {
    return "Circle"
}

// 矩形
type Rectangle struct {
    l int
    b int
}

func (t *Rectangle) accept(v Visitor) {
    v.visitForrectangle(t)
}

func (t *Rectangle) getType() string {
    return "rectangle"
}

visitor.go - 访问者接口

1
2
3
4
5
6
7
package main

type Visitor interface {
    visitForSquare(*Square)
    visitForCircle(*Circle)
    visitForrectangle(*Rectangle)
}

areaCalculator.go / middleCoordinates.go - 具体访问者

 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
package main

import "fmt"

// 面积计算器
type AreaCalculator struct {
    area int
}

func (a *AreaCalculator) visitForSquare(s *Square) {
    fmt.Println("Calculating area for square")
}

func (a *AreaCalculator) visitForCircle(s *Circle) {
    fmt.Println("Calculating area for circle")
}

func (a *AreaCalculator) visitForrectangle(s *Rectangle) {
    fmt.Println("Calculating area for rectangle")
}

// 中点坐标计算器
type MiddleCoordinates struct {
    x int
    y int
}

func (a *MiddleCoordinates) visitForSquare(s *Square) {
    fmt.Println("Calculating middle point coordinates for square")
}

func (a *MiddleCoordinates) visitForCircle(c *Circle) {
    fmt.Println("Calculating middle point coordinates for circle")
}

func (a *MiddleCoordinates) visitForrectangle(t *Rectangle) {
    fmt.Println("Calculating middle point coordinates for rectangle")
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

func main() {
    square := &Square{side: 2}
    circle := &Circle{radius: 3}
    rectangle := &Rectangle{l: 2, b: 3}

    areaCalculator := &AreaCalculator{}

    square.accept(areaCalculator)
    circle.accept(areaCalculator)
    rectangle.accept(areaCalculator)

    fmt.Println()
    middleCoordinates := &MiddleCoordinates{}
    square.accept(middleCoordinates)
    circle.accept(middleCoordinates)
    rectangle.accept(middleCoordinates)
}

输出:

1
2
3
4
5
6
7
Calculating area for square
Calculating area for circle
Calculating area for rectangle

Calculating middle point coordinates for square
Calculating middle point coordinates for circle
Calculating middle point coordinates for rectangle

总结

类型模式核心思想
创建型Singleton全局唯一实例
Factory Method由工厂决定创建哪种产品
Abstract Factory创建相关对象家族
Builder逐步构建复杂对象
Prototype克隆现有对象
结构型Adapter接口适配
Bridge抽象与实现分离
Composite树形结构统一处理
Decorator动态添加行为
Facade简化复杂系统接口
Flyweight共享对象减少内存
Proxy控制对象访问
行为型Chain of Responsibility请求沿链传递
Command将请求封装为对象
Iterator统一遍历接口
Mediator组件通过中介通信
Memento保存和恢复状态
Observer订阅-通知机制
State状态决定行为
Strategy算法可互换
Template Method算法骨架固定,步骤可变
Visitor为类层次结构添加新行为

**参考**: 本文所有代码示例整理自 [Refactoring.Guru - Design Patterns in Go](https://refactoring.guru/design-patterns/go),原始代码仓库位于 [GitHub - RefactoringGuru/design-patterns-go](https://github.com/RefactoringGuru/design-patterns-go)