5.单例模式¶
一 单例模式简介¶
单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
单例模式要实现的效果就是,对于应用单例模式的类,整个程序中只存在一个实例化对象
go并不是一种面向对象的语言,所以我们使用结构体来替代
二 代码¶
2.1 开发代码¶
package singleton
import (
"fmt"
"sync"
)
// 懒汉模式, 再需要的时候进行初始化, 相较于饿汉模式比较块
type Deploy struct {
name string
image string
}
var d *Deploy
func CreateDeploy() *Deploy {
if d == nil {
d = new(Deploy)
}
return d
}
func DeployTest() {
dObj := CreateDeploy()
dObj.name = "dname"
dObj.image = "dimage"
fmt.Println(dObj.name,dObj.image)
d2 := CreateDeploy()
fmt.Println("懒汉模式:",d2.name)
}
// 饿汉模式,初始化包的时候就使用,但是相对比较安全
type Service struct {
name string
}
var s *Service
func init() {
s = new(Service)
s.name = "sname"
}
func getService() *Service {
return s
}
func ServiceTest() {
sObj := getService()
fmt.Println("饿汉模式:",sObj.name)
}
// 单独例模式
type SinglePod struct {
name ,image string
}
var pod1 *SinglePod
var once sync.Once
func GetPod() *SinglePod {
once.Do(func() {
pod1 = new(SinglePod)
pod1.name = "podname"
pod1.image = "podimage"
})
return pod1
}
func PodTest() {
fmt.Println("单例子模式")
p1 := GetPod()
fmt.Println(p1.name, p1.image)
p2 := GetPod()
fmt.Println(p2.name, p2.image)
}
2.2 测试¶
package singleton
import "testing"
// 懒汉模式
func TestCreateDeploy(t *testing.T) {
DeployTest()
}
// 饿汉模式
func TestServiceTest(t *testing.T) {
ServiceTest()
}
// 单例模式
//
func TestPodTest(t *testing.T) {
PodTest()
}
三 应用场景¶
- 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。
- 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
- 在一个系统中要求一个类只有一个实例时才应当使用单例模式。反过来,如果一个类可以有几个实例共存,就需要对单例模式进行改进,使之成为多例模式
四 特点¶
4.1 优点¶
- 你可以保证一个类只有一个实例。
- 你获得了一个指向该实例的全局访问节点。
- 仅在首次请求单例对象时对其进行初始化。
4.2 缺点¶
- 违反了_单一职责原则_。 该模式同时解决了两个问题。
- 单例模式可能掩盖不良设计, 比如程序各组件之间相互了解过多等。
- 该模式在多线程环境下需要进行特殊处理, 避免多个线程多次创建单例对象。
- 单例的客户端代码单元测试可能会比较困难, 因为许多测试框架以基于继承的方式创建模拟对象。 由于单例类的构造函数是私有的, 而且绝大部分语言无法重写静态方法, 所以你需要想出仔细考虑模拟单例的方法。 要么干脆不编写测试代码, 或者不使用单例模式。