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()
}
相关推荐
程序员爱钓鱼5 小时前
Go语言实战案例——进阶与部署篇:编写Makefile自动构建Go项目
后端·算法·go
该用户已不存在6 小时前
别再用 if err != nil 了,学会这几个技巧,假装自己是Go大神
后端·go
李广坤7 小时前
状态模式(State Pattern)
设计模式
n8n8 小时前
Go语言操作Redis全面指南
go
李广坤8 小时前
观察者模式(Observer Pattern)
设计模式
李广坤9 小时前
中介者模式(Mediator Pattern)
设计模式
李广坤9 小时前
迭代器模式(Iterator Pattern)
设计模式
李广坤10 小时前
解释器模式(Interpreter Pattern)
设计模式
会跑的葫芦怪10 小时前
Go语言操作Redis
开发语言·redis·golang
静谧之心12 小时前
在 K8s 上可靠运行 PD 分离推理:RBG 的设计与实现
云原生·容器·golang·kubernetes·开源·pd分离