《设计模式:可复用面向对象软件的基础》之单例模式

单例模式

意图

保证一个类仅有一个实例,并提供一个访问他的全局访问点

动机

这个类可以保证没有其他实例可以被创建(通过截取创建新对象的请求),并且它可以提供一个访问该实例的方法

适用性

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时
  • 当这个危已实例应该时通过子类化可拓展的,并且客户应该无需更改代码就能使用一个拓展的实例时

参与者

  • Singleton
    • 定义一个 Instance 操作,准许客户访问它的唯一实例
    • 可能负责创建他自己的唯一实例

协作

客户只能通过 Singleton 的 Instance 操作访问一个 Singleton 实例

效果

  1. 对唯一实例的受控访问:应为 Singleton 类封装她的唯一实例,所以它可以严格的控制客户怎样以及何时访问它。
  2. 缩小名字空间:Singleton 模式是对全局变量的一种改进,它避免了那些存储唯一实例的全局变量污染名字空间。
  3. 准许对操作和表示的精化:Singleton 类可以有子类,而且这个拓展类的实例来配置一个应用是很容易的。你可以用你所需要的类的实例在运行时配置应用。
  4. 允许可变数目的实例:这个模式是你易于改变你自己的想法,并允许 Singleton 类的多个实例。此外,你可以用相同的方法来控制应用所使用的实例数目。只有允许 Singleton 实例的操作需要改变。
  5. 比类操作更灵活:另外一种封装单件功能的方式是使用操作

实现

单例实现由两种方式,分别是饿汉模式和懒汉模式

  1. 饿汉模式
type single struct {}
var instance *single
instance = &single{}

// instance 根本没有使用就被创建了
func GetSingle() *single{
    return instance
}

2、懒汉模式

// 非安全模式
type single struct{}
var instance *single
func GetSingle() *single{
    if instance == nil{
        fmt.Println("创建实例")
        instance = &single
    }
    return instance 
}

// 在 goroutine 没有锁的情况下会打印多个创建实例
for i := 0 ; i < 10; i++{
    go GetSingle()
}

// 安全模式加锁
var m sync.Mutex 
func GetSingleLock() *single {
    m.Lock()
    if instance == nil{
        instance == &single{}
    }
    m.Unlock()
    return instance
}

3、 Golang 专属单例实现

type single struct{}
var instance *single
// sync.Once 可以保证在并发时 once.Do() 中的方法只被执行一次
var once sync.Once
func GetSingle() *single{
    once.Do(func(){
        instance = &single{}
    })
    return instance
}