Go笔记··By/蜜汁炒酸奶

Go常用设计模式简记-创建型模式

创建型模式(Creational Patterns) 提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象

单例模式(Singleton Pattern)

常用场景:全局共享一个实例,且只需要被初始化一次的场景。例如数据库实例、全局配置、全局任务池等。

单例模式有利于减少内存开支、减少系统性能开销、防止多个实例产生冲突等优点。

饿汉模式

饿汉方式指全局的单例实例在包被加载时创建。

因为实例是在包被导入时初始化的,所以如果初始化耗时,会导致程序加载时间比较长。

package singleton

type singleton struct {

}

var ins *singleton= &singleton{}
// 懒汉模式
func GetIns() *singleton {
	return ins
}
1
2
3
4
5
6
7
8
9
10
11

懒汉模式

懒汉方式指全局的单例实例在第一次被使用时创建。

非线程安全版

缺点:非线程安全。当正在创建时,有线程来访问此时ins = nil就会再创建,单例类就会有多个实例了。

package singleton

type singletona struct {}

var insa *singletona
// 懒汉模式-非线程安全
func GetInsa()  *singletona {
	if insa == nil {
		insa = &singletona{}
	}
	return insa
}

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

加锁

虽然解决并发的问题,但每次加锁是要付出代价的,同时使用 defer 将导致锁变成函数级别的锁,其实只锁变量及其初始化就可以了。

package singleton
import "sync"

type singletond struct {

}

var insd *singletond
var mud sync.Mutex
// 懒汉模式加锁
func Getinsd() *singletond {
	mud.Lock()
	defer mud.Unlock()
	if insd == nil {
		insd = &singletond{}
	}
	return insd
}

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

双重检测

通过双重检测,避免了每次加锁,不用defer,避免了函数级别的锁, 从而提高代码效率。

package singleton

import "sync"

type singletonb struct {

}

var insb *singletonb
var mu sync.Mutex
// 双重检测实现懒加载
func Getinsb() *singletonb {
	if insb == nil {
		mu.Lock()
		if insb == nil{
			insb = &singletonb{}
		}
		mu.Unlock()
	}
	return insb
}

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

sync.Once实现

Golang 中特有方式,使用 sync.Once 以确保 ins 实例全局只被创建一次,once.Do 函数还可以确保当同时有多个创建动作时,只有一个创建动作在被执行

package singleton

import "sync"

type singletonc struct {

}

var insc *singletonc
var once sync.Once
// 使用once.Do可以确保 ins 实例全局只被创建一次,once.Do 函数还可以确保当同时有多个创建动作时,只有一个创建动作在被执行。
func GetInsc()  *singletonc{
	once.Do(func() {
		insc = &singletonc{}
	})
	return insc
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

工厂模式

常用场景:

  • 简单工厂模式可以传入参数并返回一个结构体的实例。
  • 抽象工厂模式可以返回一个接口,通过返回接口,在不公开内部实现的情况下,让调用者使用你提供的各种功能。
  • 工厂方法模式将对象创建从由一个对象负载所有具体类的实例化,变成由一群子类来负责对具体类的实例化,从而将过程解耦。

简单工厂模式

  • 在简单工厂模式中,依赖于唯一的工厂对象,如果我们需要实例化一个产品,就要向工厂中传入一个参数,获取对应的对象;
  • 如果要增加一种产品,就要在工厂中修改创建产品的函数。这会导致耦合性过高。
package main

import "fmt"

type Drink struct {
	name string
	size string
}

func (d Drink) DrinkInfo() {
	fmt.Println("This drink is a %s - %s",d.size,d.name)
}
// 简单工厂
// 确保创建的实例具有需要的参数。
func NewDrink(name string, size string) *Drink {
	return &Drink{
		name: name,
		size: size,
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

抽象工厂模式

  • 抽象工厂,返回类型是接口,而不是具体结构体
  • 通过返回接口,可以实现多个工厂函数,来返回不同的接口实现
  • 简单工厂和抽象工厂返回实例对象时,都可以返回指针,也可以返回非指针的实例。
  • 实际开发中建议返回非指针的实例。因为一般是想通过创建实例调用其提供的方法,而不是对实例做修改。
  • 若想修改,可通过设置Setter方法实现.
  • 通过返回非指针实例,可确保实例的属性,避免属性被意外/任意修改。
package main

import "fmt"

type Drink2 interface {
	DrinkInfo2()
}


// 果汁结构体
type juice struct {
	Name string
	Size string
}

func (j juice) DrinkInfo2()  {
	fmt.Println("This drink is a %s - %s",j.Size,j.Name)
}

func NewJuice(name string,size string) Drink2  {
	return juice{
		Name: name,
		Size: size,
	}
}
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"

type Drink3 struct {
	name string
	size string
}

func (d Drink3) DrinkInfo3() {
	fmt.Printf("This drink is a %s - %s\n",d.size,d.name)
}
 
func NewDrinkFactory(size string) func(name string) Drink3 {
	return func(name string) Drink3 {
		return Drink3{
			name: name,
			size: size,
		}
	}
}

func main() {
	// 大杯
	newLarge := NewDrinkFactory("large")
	largejuice :=newLarge("apple")
	// 中杯
	newMedium := NewDrinkFactory("medoim")
	mediumJuice := newMedium("apple")
	largejuice.DrinkInfo3()
	mediumJuice.DrinkInfo3()
}
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

参考资料

Go 语言项目开发实战

预览
Loading comments...
0 条评论

暂无数据

example
预览