上一篇笔记中,水水地学完 Golang 基本语法。开挖第二小坑,近身接触 Golang 的设计模式。这书的第二章中,我们主要学习单例模式、建造者模式、工厂模式、原型模式、抽象工厂模式这五大创建型设计模式,加油鸭…
正文
Chapter 2: Creational Patterns - Singleton, Builder, Factory, Prototype, and Abstract Factory Design Patterns
单例模式 singleton design pattern
单例模式是指在整个程序中有且仅有一个实例。
描述 Description
单例模式是非常容易记忆的。如其名称所表明的,它负责提供一个实例对象,并且没有其他复制。
在一开始引用这个对象时,该对象被创建,并在程序中所有需要这种特性的地方使用。你在许多不同的场景下需要使用单例模式,比如:
- 当你想只使用相同一个链接去连接数据库,来进行查询
- 当你打开一个 Secure Shell(SSH),去连接一台服务器,进行一系列任务操作,同时你不想为每个单一任务重新打开那个连接
- 如果你想限制对某个变量或者空间的访问通道,你可以用单例来作为那个变量的入口,在后面你可以看到这个需求采用 Go 的通道 channels 可以更好地实现
- 你想限制想一些地方发送请求的个数,你可以为接收请求窗口创建一个单例来发送请求
目标 Objectives
一般地讲,当我们受如下规则限制时,我们需要考虑使用单例模式:
- 我们需要一个全局都可以共享的特定类型的单一数据
- 在整个程序中,我们需要限制某个数据类型实例的个数是一个
例子 Example - a unique counter
如题,一个全局计数器
要求和验收标准
对于该全局计数器有如下要求:
- 当之前没有计数器被创建时,创建新的计数器,初始值为 0
- 如果已经有计数器被创建,返回那个计数器,并保持原有的值
- 如果 AddOne, 计数器的值加一
为此,针对这三个点,我们需要写测试。
单元测试 first
先新建项目路径
1 | mkdir -p $GOPATH/src/github.com/Blackstone123/go-design-patterns/creational/singleton |
新建 singleton.go
1 | package singleton |
新建 singleton_test.go
1 | package singleton |
执行 go test -v,测试不通过,意料之中。
实现 Implementation
我们需要补全代码,让代码通过测试。不像 java 和 C++,可以利用关键字 static 去实现单例模式。Go 利用包来实现这一点,更新 singleton.go 代码:
1 | package singleton |
重新运行测试指令 go test -v or go test -cover,测试通过。
几点注意:
- 在 java 或者 C++ 中,在程序开始运行时,变量实例可以被初始化为 NULL。但在 Go 中,你虽然可以初始化指针变量为 nil,但是却不能初始化一个结构体为 nil。所以注意下
var instance *singleton
,这里定义了一个指向 singleton 结构体的指针,同时初始化为 nil。 - 由于 instance 是指针,所以可以判断和 nil 是否相等,相等则用 new 关键字进行创建,这个 new 会返回创建对象的指针。也就是和 instance 类型一直,才能赋值。如果 instance 不等于 nil,则返回 instance 指针
- 这里为什么定义了 Singleton 接口,并且在 GetInstance 返回的这个接口,而不是 singleton 指针,我暂时有些搞不明白。
小结
摘自书中对本小节例子的三点小结:
单例功能
We have seen a very simple example of the Singleton pattern, partially applied to some situation, that is, a simple counter. Just keep in mind that the Singleton pattern will give you the power to have a unique instance of some struct in your application and that no package can create any clone of this struct.
封装
With Singleton, you are also hiding the complexity of creating the object, in case it requires some computation, and the pitfall of creating it every time you need an instance of it if all of them are similar. All this code writing, checking if the variable already exists, and storage, are encapsulated in the singleton and you won’t need to repeat it everywhere if you use a global variable.
线程不安全
Here we are learning the classic singleton implementation for single threaded context. We will see a concurrent singleton implementation when we reach the chapters about concurrency because this implementation is not thread safe!
总结
又水一篇,加油!