单例模式是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。
使用场景
简单地过一下单例模式的场景,用几个例子来帮助我们理解:
- 资源共享:假设一个多线程的 web 服务,每当有客户端连接时,都需要访问配置管理器,来获取配置中定义的数据库连接参数、日志记录级别等。我们知道配置信息的加载和解析可能会消耗一定的资源和时间,如果每次有新连接时都创建新的配置管理器实例,极大可能会导致资源过度消耗。这时使用单例模式,我们可以确保只有一个配置管理器实例存在,所有线程都共享同一个配置管理器实例,从而避免资源浪费、重复创建对象的性能问题。
- 控制对象的创建:比如使用日志记录器时,我们希望在整个应用程序中使用同一个日志记录器实例来记录日志信息。通过使用单例模式,我们可以限制日志记录器类的实例化次数为1,确保所有的地方都使用这一个日志记录器实例,这样就可以使日志的管理更加统一和一致。
- 全局访问点:想象一个游戏中的全局数据管理器,负责管理玩家数据、游戏状态等信息。通过使用单例模式,我们可以在任何需要访问全局数据的地方都通过统一的访问点来获取数据管理器实例,这样可以简化代码,提高代码的可维护性和扩展性。
代码实现
单例模式的实现有两种不同的思想:
- 饿汉式
- 懒汉式
饿汉式
饿汉式单例模式的设计思路:先创建,“饿了”的就能马上“吃”。
代码实现其实很简单,初始化时创建结构体,然后提供一个全局访问方法即可。
1 | package singleton |
懒汉式
懒汉式单例模式的设计思路:用的时候再来创建。
代码实现,在 go 中有两种:
- 双重检查锁定
- sync.Once(go 特色)
双重检查锁定
1 | package singleton |
sync.Once
1 | package singleton |
结语
单例模式是我们日常开发中,较为常用的设计模式。实现起来很简单,但是也有一些小细节需要注意。