首先问大家一个问题,你们面试的时候,面试官有没有问过你们:"你都用过什么设计模式?",我猜多数人的回答会把单例模式,放在第一位。

我:"呃… 我用过单例、工厂、观察者,反向代理,装饰器,哨兵"…. ",

面试官内心OS:"我都没用过这么多...反向代理是什么鬼,这小子背串了吧,不管了先就坡下驴,从头开始问"。

面试官:"用过的挺多哈,那么你能说下单例你都在什么情况下用,顺便在这张纸上实现一下单例吧"。

我:"当需要确保一个类型,只有一个实例时就需要使用单例模式了"。

面试官:"好,那你在纸上实现一下"

十分钟后的我:"不好意思,我们之前项目里都封装好了,我只用过,没有机会实现,所以..."

面试官内心OS:"好吧,这个面试KPI要求得进行三十分钟,这还有小二十分钟呢,随便再问问,就让他回去等信儿吧"

面试卒...

上面是我给大家编的一个场景,如有雷同,请憋住,不要在工位上笑喷~。单例模式虽然简单,不过还是有一些说道儿的,一是应用比较广泛,再来如果不注意容易在多线程环境下造成BUG,今天就给大家简单说下单例模式的应用,以及用Go语言怎么正确地实现单例模式。

单例模式

​单例模式是用来控制类型实例的数量的,当需要确保一个类型只有一个实例时,就需要使用单例模式。​
​GetInstance​
​饿汉模式​​懒汉模式​

下面我们用 Go 代码把这两种单例模式实现一下。

饿汉模式

​init​

如果你想了解 Go init 函数的方方面面,可以看以前的老文章​​Go语言init函数你必须记住的六个特征​​

下面用单例模式返回数据库连接实例,相信你们在项目里都见过类似代码。

这里初始化数据库的细节咱们就不多费文笔了,实际情况肯定是从配置中心加载下来数据库连接配置再实例化数据库的连接对象。这里有人可能会有个问题,你这一个程序进程就只有一个数据连接实例,那这么多请求都用一个数据库连接行吗?

诶,这个是对数据库连接的抽象呀,这个实例会维护一个连接池,那里才是真正去请求数据库用的连接。是不是有点晕,有点晕去看看你们项目里这块的代码。一般会看到初始化实例时,让你设置最大连接数、闲置连接数和存活时间这样的连接池配置。

懒汉模式

​volatile​​synchronized​
​instance​​volatile​​synchronized​​instance​​volatile​
​volatile​​atomic.Load​​atomic.Store​

如果 Go 原子操作你还不熟,请看老文章​​Golang 五种原子性操作的用法详解​​

​Go​​sync​​Once​
​sync.One ​​Once​

总结

这篇文章其实是把单例模式的应用,和Go的单例模式版本怎么实现给大家说了一下,现在教程大部分都是用 Java 讲设计模式的,虽然我们可以直接翻译,不过有的时候 Go 有些更native 的实现方式,让实现更简约一些。之前还写了一个观察者模式的文章,拒绝Go代码臃肿,其实在这几块可以用下观察者模式。


如果您的朋友也在学习 Go 语言,相信这篇文章对 TA 有帮助,欢迎转发分享给 TA,非常感谢!