文章目录
- 一、单例模式(创建型)
- 1、饿汉式
- 2、懒汉式
- 3、双重检验锁(DCL)
- 4、sync.once实现单例
- 二、工厂模式(创建型)
- 1、简单工厂模式
- 2、工厂方法模式
- 3、抽象工厂模式(暂时不写)
- 三、装饰模式(结构型)
- 四、原型模式(创建型)
- 五、观察者模式(行为)
- 六、适配器模式(结构型)
- 七、职责链模式(行为)
- 八、迭代器模式(行为)
- 九、生成器模式(创建型)
总共有22种设计模式,按照大类可分为:
行为模式:负责对象间的高效沟通和职责委派。
创建型模式:提供创建对象的机制, 能够提升已有代码的灵活性和可复用性。
结构型模式:介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。
一、单例模式(创建型)
1、饿汉式
package hungry
type singleton struct {}
// 在类加载时, 就已经初始化了, 如果singleton结
// 构体很大,那么如果用不到就初始化了,非常浪费资源
var instance = &singleton{}
func GetInstance() *singleton {
return instance
}
2、懒汉式
package lazy
type singleton struct {}
var instance *singleton
// 在需要的时候才初始化, 避免浪费资源
func GetInstance() *singleton {
// 加锁是为了解决并发问题
m.Lock()
defer m.Unlock()
if instance == nil {
instance = &singleton{}
}
return instance
}
上面这样加锁是可以解决问题,但是太重了, 程序性能下降很严重。因此出现了下面的double checking lock。
3、双重检验锁(DCL)
package dcl
import "sync"
type singleton struct {}
var instance *singleton
var m sync.Mutex
func GetInstance() *singleton {
// 如果已经不为空就不进入加锁阶段了, 这样就优化了
if instance == nil {
m.Lock()
defer m.Unlock()
if instance == nil {
// jvm new对象, 分为下面三步(即三个cpu指令)
// 1. 给 instance 分配内存
// 2. 调用 Singleton 的构造函数来初始化成员变量
// 3. 将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)
// 其中存在cpu指令重排、cpu cache的问题, 因此需要使用volatile修饰instance
// 这样就会使用主存, 而不是使用cpu cache, 避免了指令重排的尴尬
instance = &singleton{}
}
}
return instance
}
golang内存模型:https://blog.csdn.net/Zerore/article/details/120321985,这一块目前还不太清楚,大致看了下,我觉得golang中也是存在指令重排的,可以使用sync.once原子语言解决指令重排。
4、sync.once实现单例
package once
import "sync"
type singleton struct{}
var instance *singleton
var once sync.Once
func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}
二、工厂模式(创建型)
1、简单工厂模式
package simple
type Operator interface {
GetResult() float64
Init(numberA, numberB int)
}
// 运算类
type operation struct {
NumberA int
NumberB int
}
func (o *operation) Init(numberA, numberB int) {
o.NumberA = numberA
o.NumberB = numberB
}
type operationAdd struct {
operation
}
func (a *operationAdd) GetResult() float64 {
return float64(a.NumberA + a.NumberB)
}
type operationSub struct {
operation
}
func (s *operationSub) GetResult() float64 {
return float64(s.NumberA - s.NumberB)
}
// 简单工厂类
type CreateOperationFactory struct {
}
// 简单工厂类中只有一个根据参数生产对应产品的方法
func (cof *CreateOperationFactory) CreateOperate(operate string) Operator {
var op Operator
switch operate {
case "+":
op = &operationAdd{}
case "-":
op = &operationSub{}
}
return op
}
客户端代码:
package main
import (
"fmt"
"factory/simple"
)
func main() {
cof := new(simple.CreateOperationFactory)
op := cof.CreateOperate("-")
op.Init(5, 2)
fmt.Print(op.GetResult())
}
弊端:违反了开放-封闭原则(对扩展是开放的,而对修改是封闭的),每次增加新产品(比如增加乘除法支持)都需要修改工厂类中的switch语句。
2、工厂方法模式
为了符合开放-封闭原则,于是诞生了工厂方法模式。
核心思想:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类
。
package method
type Operator interface {
GetResult() float64
Init(numberA, numberB int)
}
type operation struct {
NumberA int
NumberB int
}
func (o *operation) Init(numberA, numberB int) {
o.NumberA = numberA
o.NumberB = numberB
}
type operationAdd struct {
operation
}
func (a *operationAdd) GetResult() float64 {
return float64(a.NumberA + a.NumberB)
}
type operationSub struct {
operation
}
func (s *operationSub) GetResult() float64 {
return float64(s.NumberA - s.NumberB)
}
// 相比简单工厂模式, 上面都是一模一样, 只是多了一堆
// 工厂子类, 创建获取产品对象的操作委托给了工厂子类去做
type IFactory interface {
CreateOperation() Operator
}
type AddFactory struct {}
type SubFactory struct {}
func (af *AddFactory) CreateOperation() Operator {
return &operationAdd{}
}
func (sf *SubFactory) CreateOperation() Operator {
return &operationSub{}
}
客户端代码:
package main
import (
"fmt"
"design-mode/method"
)
func main() {
var operFactory method.IFactory = &method.SubFactory{}
oper := operFactory.CreateOperation()
oper.Init(5, 2)
fmt.Print(oper.GetResult())
}