设计模式——单例模式

161 阅读4分钟

单例模式

类型

创建型设计模式

核心思想

单例模式的核心思想是通过限制某个类只能创建一个对象来达到全局唯一的目的。

概述

单例模式限制一个类只能创建一个对象,并提供一个全局访问点来访问该对象。保证全局只有一个对象实例,避免了多个对象实例导致的资源浪费或竞争问题,同时也可以提高代码的可维护性和可测试性。常见的实现方式有懒汉和饿汉方式

结构图

image.png

场景

一些需要共享资源或管理全局状态的场景中比较常用

  1. 配置管理器
  2. 日志记录器
  3. 数据库实例
  4. 缓存管理器
  5. 线程池

主要角色

  1. 单例类(Singleton Class):该类是单例模式的核心,负责实现单例模式的逻辑,通常包含一个私有的构造函数、一个静态的实例变量和一个静态的获取实例的方法。该类确保只有一个实例被创建,并提供全局唯一的访问点来访问该实例。(在golang中可以使用sync.once保证实例只创建一次)
  2. 客户端(Client):客户端是单例模式的使用者,通过调用单例类提供的方法来获取实例,并对实例进行操作。客户端不直接创建单例类的实例,而是通过单例类提供的访问点来获取唯一的实例。

优缺点

优点

  1. 全局唯一性:单例模式可以确保一个类只有一个实例对象,避免了重复创建对象带来的内存浪费和性能下降的问题。
  2. 提高系统性能:单例模式可以在系统中缓存重要的资源和数据,减少重复创建和销毁对象的开销,从而提高系统性能和响应速度。
  3. 简化代码逻辑:使用单例模式可以简化代码逻辑,避免大量的全局变量和静态方法的使用,使代码结构更加清晰和易于维护

缺点

  1. 可能引起并发问题:在多线程环境下,如果单例模式没有进行合适的并发控制,可能会引起多个线程同时访问和修改单例对象的问题,从而导致程序的运行错误和不可预期的结果。
  2. 不利于扩展:单例模式的实现通常使用静态方法和静态变量,这使得单例对象的创建和访问紧密耦合在一起,不利于扩展。
  3. 对OOP特性的支持不足:单例模式的实现通常违背了OOP的一些原则,比如开放封闭原则和依赖倒置原则,可能导致代码的可维护性和可扩展性下降。

单例模式的线程安全

基于线程安全,使用doublecheck机制或者sync.once来保证单例模式的线程安全

doubleCheck

package singleton

import "sync"

type singleton struct {
    // ...
}

var instance *singleton
var mutex sync.Mutex

func GetInstance() *singleton {
    // Double Check Locking
    if instance == nil {
        mutex.Lock()
        if instance == nil {
            instance = &singleton{}
        }
        mutex.Unlock()

    }
    return instance
}

sync.Once

package singleton

import "sync"

type singleton struct {
    // ...
}

var instance *singleton
var once sync.Once

func GetInstance() *singleton {
    once.Do(func() {
        instance = &singleton{}
    })
    return instance
}

两种实现区别及demo

饿汉模式

在程序启动时或单例类被加载时,就已经创建了单例对象,并且在后续调用 GetInstance 方法时直接返回该对象。

特点

饿汉单例模式的实现可以在程序启动时就创建对象,不需要考虑线程安全问题,而且在一开始就初始化完成,可以方便后面使用。但如果初始化代价较高,但是使用频率极低,饿汉模式会造成性能和内存的浪费。

demo

package singleton

type singleton struct {
    // ...
}

var instance = &singleton{} // 在包初始化时就初始化单例对象

// GetInstance 返回单例对象
func GetInstance() *singleton {
    return instance
}

懒汉模式

单例对象的创建被延迟到第一次调用 GetInstance 方法时。在这之前,单例对象并不存在,只有一个空的单例类。

特点

懒汉单例模式只有在第一次使用时才会创建单例对象,因此可以避免在程序启动时就创建对象,从而减少启动时间和提高性能。但是,懒汉单例模式的实现需要考虑线程安全问题,通常需要使用锁或同步机制来保证线程安全。

demo

package singleton

import "sync"

type singleton struct {
    // ...
}

var (
    instance *singleton
    once     sync.Once
)

// GetInstance 返回单例对象
func GetInstance() *singleton {
    once.Do(func() {
        instance = &singleton{} // 在第一次调用 GetInstance 方法时才初始化单例对象
    })
    return instance
}