go设计模式-单例设计模式

单例模式解决什么问题

  • 处理资源访问冲突
  • 表示全局唯一. 在整个程序中,某些数据应该存储一份,这样的类,就应该设计成单例类。比如,SpringBoot应用的配置项。再比如 雪花算法的实现类。

实现单例

如何实现单例呢,或者说怎样能称得上单例呢?
首先,单例要求全局唯一,那就是说 不允许外部随随便便new一个新对象.

这句话里,至少有两个点:
1.不允许外部new对象。 2.需要类内部new对象,并提供给外部进行使用。
其实很简单的,单例类的构造方法需要是private权限的,提供一个public方法,用于外部获取本对象。

另外,我们写的程序都是运行在多线程环境的,所以我们还需要考虑线程安全的问题。
还有,说过线程安全,自然而然,就会想到锁,想到锁,就要想到性能问题。

核心概念

保证某个类在一个运行环境中只有一个实例, 并提供一个访问该实例的全局节点。

单例的实现

懒汉式

package singleton
import "sync"
var (
lazySingletonInstance *lazySingleton
once = &sync.Once{}
)
// LazySingleton 懒汉式
type lazySingleton struct {
}
func GetLazySingletonInstance() *lazySingleton {
if lazySingletonInstance == nil {
once.Do(func() {
lazySingletonInstance = &lazySingleton{}
})
}
return lazySingletonInstance
}

测试代码

package singleton
import "testing"
func TestGetLazySingletonInstance(t *testing.T) {
if GetLazySingletonInstance() != GetLazySingletonInstance() {
t.Error("懒汉式 单例模式: 出现同一对象")
}
}
func BenchmarkGetLazySingletonInstance(b *testing.B) {
for i := 0; i < b.N; i++ {
if GetLazySingletonInstance() != GetLazySingletonInstance() {
b.Error("懒汉式 单例模式: 出现同一对象")
}
}
}

饿汉式

package singleton
import "fmt"
type eagerSingleton struct {
}
var eagerSingletonObj *eagerSingleton
// init. 程序运行前的注册,实现 sync.Once功能 ,初始化不能采用初始化表达式初始化的变量。
// 变量初始化->init()->main()
func init() {
fmt.Println("init in main.go ")
eagerSingletonObj = &eagerSingleton{}
}
func GetInstance() *eagerSingleton {
return eagerSingletonObj
}

测试代码

package singleton
import "testing"
func TestEagerSingleton(t *testing.T) {
if GetInstance() != GetInstance() {
t.Error("单例模式-饿汉式:出现不同的对象")
}
}
func BenchmarkGetInstance(b *testing.B) {
for i := 0; i < b.N; i++ {
if GetInstance() != GetInstance() {
b.Error("单例模式-饿汉式:出现不同的对象")
}
}
}

双重检查锁

package singleton
import (
"fmt"
"sync"
)
type doubleCheckSingleton struct {
}
var lock = &sync.Mutex{}
var doubleCheckSingletonInstance *doubleCheckSingleton
func GetDoubleCheckSingletonInstance() *doubleCheckSingleton {
if doubleCheckSingletonInstance == nil {
lock.Lock()
defer lock.Unlock()
if doubleCheckSingletonInstance == nil {
fmt.Println("创建 单例对象")
doubleCheckSingletonInstance = &doubleCheckSingleton{}
} else {
fmt.Println("单例对象已经创建了")
}
} else {
fmt.Println("单例对象已经创建了")
}
return doubleCheckSingletonInstance
}

测试代码

package singleton
import "testing"
func TestGetDoubleCheckSingletonInstance(t *testing.T) {
if GetDoubleCheckSingletonInstance() != GetDoubleCheckSingletonInstance() {
t.Error("双重检测法 获取不同的对象")
}
}
func BenchmarkGetDoubleCheckSingletonInstance(b *testing.B) {
for i := 0; i < b.N; i++ {
if GetDoubleCheckSingletonInstance() != GetDoubleCheckSingletonInstance() {
b.Error("双重检测法 获取不同的对象")
}
}
}

问题

1、单例模式同事解决了两个问题,所以单例模式违反了单一职责原则:

  • 1、保证一个类只有一个实例.
  • 2、为该实例提供了一个全局访问节点.
    2、单例模式可能会掩盖不良设计,比如程序各组件之间项目了解过多。
    3、该模式在多线程环境下需要进行特殊处理, 避免多个线程多次创建单例对象。
    4、单例的客户端代码单元测试可能会比较困难, 因为许多测试框架以基于继承的方式创建模拟对象。 由于单例类的构造函数是私有的, 而且绝大部分语言无法重写静态方法, 所以你需要想出仔细考虑模拟单例的方法。 要么干脆不编写测试代码, 或者不使用单例模式。

与其他模式的关系

  • 外观模式类通常可以转换为单例模式类, 因为在大部分情况下一个外观对象就足够了。
  • 如果你能将对象的所有共享状态简化为一个享元对象, 那么享元模式就和单例类似了。 但这两个模式有两个根本性的不同。
    • 只会有一个单例实体, 但是享元类可以有多个实体, 各实体的内在状态也可以不同。
    • 单例对象可以是可变的。 享元对象是不可变的。
  • 抽象工厂模式、 生成器模式和原型模式都可以用单例来实现。

最后

希望和你一起遇见更好的自己

扫码或搜索:方家小白
发送 UHDCQA
即可立即永久解锁本站全部文章

本文作者:方家小白

本文链接:https://www.cnblogs.com/wxy540843763/p/15997384.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   方家小白  阅读(19)  评论(0编辑  收藏  举报
历史上的今天:
2018-03-12 散记2018-03-12
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.