创建者模式

单例模式

为什么要用单例模式

保证一个对象只有一个实例 ,减少内存开销。比如一些可以复用一个连接的网络,比如http2 client等,而且可以减少网络开销。

为什么不用个全局变量控制

因为任何代码都有可能覆盖掉那些变量的内容, 从而引发程序崩溃。

代码实现

 1package main
 2
 3import (
 4	"fmt"
 5	"sync"
 6)
 7
 8type Single struct {
 9}
10
11var single *Single
12var once = &sync.Once{}
13
14func NewSingle() *Single {
15	once.Do(func() {
16		single = &Single{
17			// 初始化
18		}
19	})
20	return single
21}
22
23func main() {
24	for i := 0; i < 1000; i++ {
25		s := NewSingle()
26		fmt.Printf("create %d,address %p\n", i, s)
27	}
28}
29
30/*
31结果:
32create 0,address 0x1164fe0
33create 1,address 0x1164fe0
34create 2,address 0x1164fe0
35create 3,address 0x1164fe0
36create 4,address 0x1164fe0
37create 5,address 0x1164fe0
38create 6,address 0x1164fe0
39create 7,address 0x1164fe0
40create 8,address 0x1164fe0
41...
42*/

工厂模式

我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。比如电脑支持intel cpu,现在要支持amd cpu,我们就可以让所有cpu实现接口。

简单工厂模式

实现简单,不适合复杂场景

 1package main
 2
 3import (
 4	"errors"
 5	"fmt"
 6)
 7
 8// 工厂接口
 9type CpuFactory interface {
10	Run() string
11}
12
13type IntelCpu struct{}
14
15func (*IntelCpu) Run() string {
16	return "intel cpu is running"
17}
18
19type AmdCpu struct{}
20
21func (*AmdCpu) Run() string {
22	return "amd cpu is running"
23}
24
25func NewCpu(name string) (CpuFactory, error) {
26	switch name {
27	case "intel":
28		return &IntelCpu{}, nil
29	case "amd":
30		return &AmdCpu{}, nil
31	default:
32		return nil, errors.New("no such cpu")
33	}
34}
35
36func main() {
37	c1, err := NewCpu("intel")
38	if err != nil {
39		panic(err)
40	}
41	fmt.Println(c1.Run())
42	c2, err := NewCpu("amd")
43	if err != nil {
44		panic(err)
45	}
46	fmt.Println(c2.Run())
47	_, err = NewCpu("other")
48	fmt.Println(err)
49}
50/*
51结果:
52intel cpu is running
53amd cpu is running
54no such cpu
55*/

工厂方法模式

大部分时候,我们创建对象要创建很多逻辑,比如初始化变量,从远端请求config等等。这是我们需要每次struct提供个创建方法。

 1package main
 2
 3import (
 4	"errors"
 5	"fmt"
 6)
 7
 8// 工厂接口
 9type CpuFactory interface {
10	Run() string
11}
12
13type IntelCpu struct{}
14
15func (*IntelCpu) Run() string {
16	return "intel cpu is running"
17}
18
19func NewIntelCpu() *IntelCpu {
20	// 做创建逻辑
21	return &IntelCpu{}
22}
23
24type AmdCpu struct{}
25
26func (*AmdCpu) Run() string {
27	return "amd cpu is running"
28}
29
30func NewAmdCpu() *AmdCpu {
31	// 做创建逻辑
32	return &AmdCpu{}
33}
34
35func NewCpu(name string) (CpuFactory, error) {
36	switch name {
37	case "intel":
38		return NewIntelCpu(), nil
39	case "amd":
40		return NewAmdCpu(), nil
41	default:
42		return nil, errors.New("no such cpu")
43	}
44}
45
46func main() {
47	c1, err := NewCpu("intel")
48	if err != nil {
49		panic(err)
50	}
51	fmt.Println(c1.Run())
52	c2, err := NewCpu("amd")
53	if err != nil {
54		panic(err)
55	}
56	fmt.Println(c2.Run())
57	_, err = NewCpu("other")
58	fmt.Println(err)
59}
60/*
61结果:
62intel cpu is running
63amd cpu is running
64no such cpu
65*/

抽象工厂模式

抽象工厂模式则是针对的多个产品等级结构, 我们可以将一种产品等级想象为一个产品族,所谓的产品族,是指位于不同产品等级结构中功能相关联的产品组成的家族。用于复杂场景,比如amdintel都生成cpugpu

 1package main
 2
 3import "fmt"
 4
 5// 抽象工厂接口
 6type ElementAbstractFactory interface {
 7	CreateCpu() Cpu // cpu
 8	CreateGpu() Gpu // Gpu
 9}
10
11func GetElementAbstractFactory(brand string) (ElementAbstractFactory, error) {
12	if brand == "intel" {
13		return &intel{}, nil
14	}
15
16	if brand == "amd" {
17		return &amd{}, nil
18	}
19
20	return nil, fmt.Errorf("Wrong brand type passed")
21}
22
23// cpu 具体工厂
24type Cpu interface {
25	Run() string
26}
27
28type Gpu interface {
29	Graphics() string
30}
31
32type intel struct{}
33
34func (*intel) CreateCpu() Cpu {
35	return &intelCpu{}
36}
37
38func (*intel) CreateGpu() Gpu {
39	return &intelGpu{}
40}
41
42type intelCpu struct{}
43
44func (*intelCpu) Run() string {
45	return "intel cpu is running"
46}
47
48type intelGpu struct{}
49
50func (*intelGpu) Graphics() string {
51	return "intel gpu is working on graphics"
52}
53
54type amd struct{}
55
56func (*amd) CreateCpu() Cpu {
57	return &amdCpu{}
58}
59
60func (*amd) CreateGpu() Gpu {
61	return &amdGpu{}
62}
63
64type amdCpu struct{}
65
66func (*amdCpu) Run() string {
67	return "amd cpu is running"
68}
69
70type amdGpu struct{}
71
72func (*amdGpu) Graphics() string {
73	return "amd gpu is working on graphics"
74}
75
76func main() {
77	e, _ := GetElementAbstractFactory("intel")
78	cpu := e.CreateCpu()
79	gpu := e.CreateGpu()
80	fmt.Println(cpu.Run())
81	fmt.Println(gpu.Graphics())
82	e2, _ := GetElementAbstractFactory("amd")
83	cpu2 := e2.CreateCpu()
84	gpu2 := e2.CreateGpu()
85	fmt.Println(cpu2.Run())
86	fmt.Println(gpu2.Graphics())
87}
88/*
89intel cpu is running
90intel gpu is working on graphics
91amd cpu is running
92amd gpu is working on graphics
93*/

生成器模式

在对其进行构造时需要对诸多成员变量和嵌套对象进行繁复的初始化工作。 这些初始化代码通常深藏于一个包含众多参数且让人基本看不懂的构造函数中; 甚至还有更糟糕的情况, 那就是这些代码散落在客户端代码的多个位置。比如在go中,就可以利用指针传递完成初始化。

 1package main
 2
 3import "fmt"
 4
 5// Computer 是生成器接口
 6type Computer interface {
 7	Cpu()
 8	Gpu()
 9}
10
11type Director struct {
12	builder Computer
13}
14
15// NewDirector ...
16func NewDirector(builder Computer) *Director {
17	return &Director{
18		builder: builder,
19	}
20}
21
22// Construct Product
23func (d *Director) Construct() {
24	d.builder.Cpu()
25	d.builder.Gpu()
26}
27
28type Computer1 struct {
29	cpu string
30	gpu string
31}
32
33func (c *Computer1) Cpu() {
34	c.cpu = "intel"
35}
36
37func (c *Computer1) Gpu() {
38	c.gpu = "nvida"
39}
40
41type Computer2 struct {
42	cpu string
43	gpu string
44}
45
46func (c *Computer2) Cpu() {
47	c.cpu = "amd"
48}
49
50func (c *Computer2) Gpu() {
51	c.gpu = "amd"
52}
53
54func main() {
55	c1 := Computer1{}
56	d := NewDirector(&c1)
57	fmt.Printf("%+v\n", c1)
58	d.Construct()
59	fmt.Printf("%+v\n", c1)
60
61	c2 := Computer2{}
62	d2 := NewDirector(&c2)
63	fmt.Printf("%+v\n", c2)
64	d2.Construct()
65	fmt.Printf("%+v\n", c2)
66}
67/*
68{cpu: gpu:}
69{cpu:intel gpu:nvida}
70{cpu: gpu:}
71{cpu:amd gpu:amd}
72*/

原型模式

原型模式使对象能复制自身,并且暴露到接口中,使客户端面向接口编程时,不知道接口实际对象的情况下生成新的对象。

原型模式配合原型管理器使用,使得客户端在不知道具体类的情况下,通过接口管理器得到新的实例,并且包含部分预设定配置。

 1package main
 2
 3//Cloneable 是原型对象需要实现的接口
 4type Cloneable interface {
 5	Clone() Cloneable
 6}
 7
 8type PrototypeManager struct {
 9	prototypes map[string]Cloneable
10}
11
12func NewPrototypeManager() *PrototypeManager {
13	return &PrototypeManager{
14		prototypes: make(map[string]Cloneable),
15	}
16}
17
18func (p *PrototypeManager) Get(name string) Cloneable {
19	return p.prototypes[name].Clone()
20}
21
22func (p *PrototypeManager) Set(name string, prototype Cloneable) {
23	p.prototypes[name] = prototype
24}

References

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

https://refactoringguru.cn/design-patterns

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