GO设计模式——4、单例模式(创建型)

目录

[单例模式(Singleton Pattern)](#单例模式(Singleton Pattern))

优缺点

使用场景

饿汉式和懒汉式单例模式


单例模式(Singleton Pattern)

单例模式(Singleton Pattern)是一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。单例模式通常适用于需要共享资源、管理全局状态或控制某个唯一实例的情况。在开发中,单例模式可以帮助我们解决数据结构与操作之间的耦合问题,实现代码的复用和扩展。

单例模式的核心思想是:通过将类的构造函数私有化,防止外部代码直接实例化该类,然后提供一个静态方法或全局变量来获取类的唯一实例。具体而言,单例模式包含以下几个关键要素:(1)私有的构造函数(Private Constructor):用于限制类的实例化,使其只能在类的内部进行。(2)静态变量或静态方法(Static Variable/Method):用于保存类的唯一实例或提供全局访问点。通过将构造函数私有化,外部代码无法直接实例化类,只能通过静态方法或全局变量来获取类的唯一实例。当第一次获取实例时,单例模式会创建一个实例,并将其保存起来,以供后续调用使用。

优缺点

(1)优点:

  • 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
  • 避免对资源的多重占用(比如写文件操作)。

(2)缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

使用场景

  • 要求生产唯一序列号。
  • WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  • 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

饿汉式和懒汉式单例模式

两种常见实现方式:饿汉式单例模式和懒汉式单例模式。饿汉式单例模式适用于在程序初始化时就需要创建实例,并且该实例在整个程序生命周期内都需要被使用的情况。例如,全局的配置管理器或系统的日志记录器可以使用饿汉式单例模式来确保全局唯一性和一致性。懒汉式单例模式适用于需要延迟实例化的情况,即只有在需要时才创建实例。例如,某些资源消耗较大的对象,或者需要根据运行时的条件来确定具体实例的情况下,可以使用懒汉式单例模式。

1. 饿汉式单例模式

饿汉式单例模式是一种在类加载时就创建唯一实例的单例模式。在这种模式下,实例在整个程序的生命周期中始终存在,无论是否被使用。在GetConfigManager()方法中返回配置管理器的唯一实例。配置管理器包含一个configData字典,用于保存配置数据。在程序初始化时创建了配置管理器的实例,并通过instance变量保存起来。

饿汉式单例模式代码实现:

Go 复制代码
package main

import "fmt"

type ConfigManager struct {
    configData map[string]string
}

var instance = &ConfigManager{
    configData: make(map[string]string),
}

func GetConfigManager() *ConfigManager {
    return instance
}

func (cm *ConfigManager) Set(key, value string) {
    cm.configData[key] = value
}

func (cm *ConfigManager) Get(key string) string {
    return cm.configData[key]
}

func main() {
    configManager := GetConfigManager()
    configManager.Set("key1", "value1")
    fmt.Println("key1:", configManager.Get("key1"))

    configManager1 := GetConfigManager()
    configManager1.Set("key1", "value2")
    fmt.Println("key1:", configManager.Get("key1"))
}

2. 懒汉式单例模式

懒汉式单例模式是一种在需要时才创建实例的单例模式。在这种模式下,实例的创建延迟到第一次被使用时才进行。使用了sync.Once来确保GetLogger()方法只被执行一次,从而保证只有一个实例被创建。日志记录器包含了一个日志列表,可以通过AddLog()方法添加日志,以及通过PrintLogs()方法打印所有日志。

懒汉式单例模式代码实现:

Go 复制代码
package main

import (
    "fmt"
    "sync"
)

type Logger struct {
    logs []string
}

var instancel *Logger
var once sync.Once

func GetLogger() *Logger {
    once.Do(func() {
       instancel = &Logger{
          logs: []string{},
       }
    })
    return instancel
}
func (l *Logger) AddLog(log string) {
    l.logs = append(l.logs, log)
}
func (l *Logger) printLogs() {
    for _, log := range l.logs {
       fmt.Println(log)
    }
}

func main() {
    logger := GetLogger()
    logger.AddLog("Log 1")
    logger.AddLog("log2")

    logger1 := GetLogger()
    logger1.AddLog("log3")

    logger.printLogs()

    logger1.printLogs()
}
相关推荐
xcLeigh7 小时前
Go入门:短变量声明的陷阱与最佳实践
java·redis·golang·教程·变量
JavaPub-rodert7 小时前
我又把自己的 Go 后台管理系统升级了一遍:文件管理、2GB 上传、私有文件预览、Docker 镜像全安排上了
开发语言·docker·golang·shiyuadmin
名字还没想好☜8 小时前
Go 的 sync.Cond 实战:用条件变量做等待/通知,比忙轮询省 CPU
开发语言·数据库·后端·golang·go
AustinXu8 小时前
从 Claude Code 到 Claude Tag,Harness Engineering 走到了组织这一层
设计模式·团队管理
剧中有戏8 小时前
单例模式从入门到精通:一个数据库连接池的完整剖析
设计模式
胡萝卜术9 小时前
编译期与运行期的双重防线:从 TypeScript 类型之争到 LLM 输出的自动化择优
前端·设计模式·面试
货拉拉技术9 小时前
重塑 Agent 度量衡:基于 LLM-as-a-Judge 的离线评估体系与实践
算法·设计模式
KhalilRuan9 小时前
设计模式小记
设计模式
卡牌RWA研究院10 小时前
Relique:一张精品卡牌如何从收藏品变成可交易的链上资产?
设计模式·金融·区块链·创业创新
程序员爱钓鱼10 小时前
Go 输入输出详解
后端·面试·go