亦称:Singleton
保证进程中一个类只有一个实例,并提供一个全局访问点。
- 单例模式能够保证类只有一个实例,即instance
- 有方法能从外部访问这个实例,即GetInstance()
- 外部无法创建实例,类的构造方法为私有,外部无法访问
使用场景
- 多线程场景访问资源,控制只有一个对象操作资源,并对该对象加锁使用
- 需要保证全局唯一的类,如配置类
实现步骤
- 在类中添加一个私有静态成员变量用于保存单例实例。
- 声明一个公有静态构建方法用于获取单例实例。
- 在静态方法中实现"延迟初始化"。该方法会在首次被调用时创建一个新对象,并将其存储在静态成员变量中。此后该方法每次被调用时都返回该实例。
- 将类的构造函数设为私有。类的静态方法仍能调用构造函数,但是其他对象不能调用。
- 检查客户端代码, 将对单例的构造函数的调用替换为对其静态构建方法的调用。
优缺点
优点
- 可以保证一个类只有一个实例。
- 仅在首次请求单例对象时对其进行初始化。
缺点
- 违反了单一职责原则,该模式同时解决了两个问题。
- 在多线程环境下需要进行特殊处理,避免多个线程多次创建单例对象。
与其它模式的关系
外观模式
类通常可以转换为单例模式类,因为在大部分情况下一个外观对象就足够了。
- 如果你能将对象的所有共享状态简化为一个
享元对象
,那么享元模式就和单例类似了。但这两个模式有两个根本性的不同。
- 只会有一个单例实体。但是享元类可以有多个实体,各实体的内在状态也可以不同。
- 单例对象可以是可变的。享元对象是不可变的。
抽象工厂模式
、生成器模式
和原型模式
都可以用单例来实现。
示例
示例一
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
|
package main
import (
"fmt"
"sync"
)
var lock = &sync.Mutex{}
type single struct {
}
var singleInstance *single
func getInstance() *single {
if singleInstance == nil {
lock.Lock()
defer lock.Unlock()
if singleInstance == nil {
fmt.Println("Creating single instance now.")
singleInstance = &single{}
} else {
fmt.Println("Single instance already created.")
}
} else {
fmt.Println("Single instance already created.")
}
return singleInstance
}
|
示例二
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
|
package main
import (
"fmt"
"sync"
)
var once sync.Once
type single struct {
}
var singleInstance *single
func getInstance() *single {
if singleInstance == nil {
once.Do(
func() {
fmt.Println("Creating single instance now.")
singleInstance = &single{}
})
} else {
fmt.Println("Single instance already created.")
}
return singleInstance
}
|
示例三
饿汉模式,仅适用于实例的早期初始化工作已经确定时。
1
2
3
4
5
6
7
8
9
10
|
package main
var singleInstance *single
type single struct {
}
func init() {
singleInstance = &single{}
}
|
References
guru