概述

单例模式是最简单的设计模式之一,属于创建型模式。该模式确保单例类(在当前进程中)只有一个实例。

在系统中,有些数据只应该保存一份,这些数据就应该设计为单例类。例如一个系统的配置数据,使用一个单例对象来访问即可。

单例模式通常需要满足如下条件:

  1. 类的构造方法为私有,即不允许其他代码实例化该类,以保证实例的唯一性。
  2. 类应该提供一个公共的静态方法来为其他代码提供该实例。
  3. 其他代码只能通过访问该类的静态方法来获取该类的唯一实例。该静态方法在未创建单例时进行创建,如果已经创建,则返回该单例(引用、指针)。

实现方式

饿汉式

饿汉式,即在加载类的时候就直接创建出类的实例,对外只暴露静态方法。

该模式使用golang实现时,利用包的init函数实现比较方便。典型的例子就是创建数据库连接实例的时候,会利用init进行初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package singleton

// 单例类
type Singleton struct {
s string // 以string举例
}

// 非导出的指针指向类的实例
var singleton *Singleton

// init 创建单例
func init() {
singleton = &Singleton{s: "test"}
}

// GetInstance 获取实例
func GetInstance() *Singleton {
return singleton
}

测试:

1
2
3
4
5
6
7
8
9
package singleton  

import "testing"

func TestGetInstance(t *testing.T) {
if GetInstance() != GetInstance() {
t.Error("Singleton instance is not unique")
}
}

优点:

  1. 实现简单,在类加载(导入包)的时候就完成实例化,避免线程同步问题。

缺点:

  1. 未实现 Lazy 初始化。
  2. 容易产生垃圾对象,若该实例从未被使用,会造成内存浪费。

懒汉式

懒汉式,即在在需要调用实例的时候再对类实例化。
为了保证并发安全,需要加锁,但是同时会大大降低效率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package singleton  

import "sync"

var (
lazySingleton *Singleton
once sync.Once
)

// GetLazySingleton 懒汉式单例模式
func GetLazySingleton() *Singleton {
// once.Do 只会在 lazySingleton 为 nil 时执行一次
once.Do(func() {
lazySingleton = &Singleton{}
})
return lazySingleton
}

测试:

1
2
3
4
5
6
7
8
9
package singleton  

import "testing"

func TestGetLazySingleton(t *testing.T) {
if GetLazySingleton() != GetLazySingleton() {
t.Error("Lazy singleton instance is not unique")
}
}

优点:

  1. 实现 Lazy 初始化。

缺点:

  1. 需要加锁以保证并发安全,导致效率降低。