常用设计模式

1. 单例模式:

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

package singleton

import (
"sync"
)

type singleton struct {
}

var ins *singleton
var once sync.Once

func GetInsOr() *singleton {
once.Do(func() {
ins = &singleton{}
})
return ins
}

2. 简单工厂模式

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

type Person struct {
Name string
Age int
}

func (p Person) Greet() {
fmt.Printf("Hi! My name is %s", p.Name)
}

func NewPerson(name string, age int) *Person {
return &Person{
Name: name,
Age: age,
}
}

3. 抽象工厂模式

它返回的是接口而不是结构体。

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

type Person interface {
Greet()
}

type person struct {
name string
age int
}

func (p person) Greet() {
fmt.Printf("Hi! My name is %s", p.name)
}

// Here, NewPerson returns an interface, and not the person struct itself
func NewPerson(name string, age int) Person {
return person{
name: name,
age: age,
}
}

4. 工厂方法模式

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

type Person struct {
name string
age int
}

func NewPersonFactory(age int) func(name string) Person {
return func(name string) Person {
return Person{
name: name,
age: age,
}
}
}

5. 策略模式

在项目开发中,我们经常要根据不同的场景,采取不同的措施,也就是不同的策略。比如,假设我们需要对 a、b 这两个整数进行计算,根据条件的不同,需要执行不同的计算方式。为了解耦,需要使用策略模式,定义一些独立的类来封装不同的算法,每一个类封装一个具体的算法(即策略)。

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 strategy

// 策略模式

// 定义一个策略类
type IStrategy interface {
do(int, int) int
}

// 策略实现:加
type add struct{}

func (*add) do(a, b int) int {
return a + b
}

// 策略实现:减
type reduce struct{}

func (*reduce) do(a, b int) int {
return a - b
}

// 具体策略的执行者
type Operator struct {
strategy IStrategy
}

// 设置策略
func (operator *Operator) setStrategy(strategy IStrategy) {
operator.strategy = strategy
}

// 调用策略中的方法
func (operator *Operator) calculate(a, b int) int {
return operator.strategy.do(a, b)
}

6. 模板模式

模板模式就是将一个类中能够公共使用的方法放置在抽象类中实现,将不能公共使用的方法作为抽象方法,强制子类去实现,这样就做到了将一个类作为一个模板,让开发者去填充需要填充的地方。

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 template

import "fmt"

type Cooker interface {
fire()
cooke()
outfire()
}

// 类似于一个抽象类
type CookMenu struct {
}

func (CookMenu) fire() {
fmt.Println("开火")
}

// 做菜,交给具体的子类实现
func (CookMenu) cooke() {
}

func (CookMenu) outfire() {
fmt.Println("关火")
}

// 封装具体步骤
func doCook(cook Cooker) {
cook.fire()
cook.cooke()
cook.outfire()
}

type XiHongShi struct {
CookMenu
}

func (*XiHongShi) cooke() {
fmt.Println("做西红柿")
}

type ChaoJiDan struct {
CookMenu
}

func (ChaoJiDan) cooke() {
fmt.Println("做炒鸡蛋")
}

##7. 代理模式
可以为另一个对象提供一个替身或者占位符,以控制对这个对象的访问。

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 proxy

import "fmt"

type Seller interface {
sell(name string)
}

// 火车站
type Station struct {
stock int //库存
}

func (station *Station) sell(name string) {
if station.stock > 0 {
station.stock--
fmt.Printf("代理点中:%s买了一张票,剩余:%d \n", name, station.stock)
} else {
fmt.Println("票已售空")
}

}

// 火车代理点
type StationProxy struct {
station *Station // 持有一个火车站对象
}

func (proxy *StationProxy) sell(name string) {
if proxy.station.stock > 0 {
proxy.station.stock--
fmt.Printf("代理点中:%s买了一张票,剩余:%d \n", name, proxy.station.stock)
} else {
fmt.Println("票已售空")
}
}

8. 选项模式

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

package options

import (
"time"
)

type Connection struct {
addr string
cache bool
timeout time.Duration
}

const (
defaultTimeout = 10
defaultCaching = false
)

type options struct {
timeout time.Duration
caching bool
}

// Option overrides behavior of Connect.
type Option interface {
apply(*options)
}

type optionFunc func(*options)

func (f optionFunc) apply(o *options) {
f(o)
}

func WithTimeout(t time.Duration) Option {
return optionFunc(func(o *options) {
o.timeout = t
})
}

func WithCaching(cache bool) Option {
return optionFunc(func(o *options) {
o.caching = cache
})
}

// Connect creates a connection.
func NewConnect(addr string, opts ...Option) (*Connection, error) {
options := options{
timeout: defaultTimeout,
caching: defaultCaching,
}

for _, o := range opts {
o.apply(&options)
}

return &Connection{
addr: addr,
cache: options.caching,
timeout: options.timeout,
}, nil
}

选项模式通常适用于以下场景:

  • 结构体参数很多,创建结构体时,我们期望创建一个携带默认值的结构体变量,并选择性修改其中一些参数的值。
  • 结构体参数经常变动,变动时我们又不想修改创建实例的函数。例如:结构体新增一个 retry 参数,但是又不想在 NewConnect 入参列表中添加retry int这样的参数声明。

参考文章

  • Go 语言项目开发实战/设计模式