当前位置: 首页 > news >正文

设计模式--单例模式(Singleton)【Go】

 引言

在设计模式中,单例模式(Singleton Pattern)是一种非常常见且实用的模式。它的核心思想是确保一个类只有一个实例,并提供一个全局访问点。这种模式在需要全局唯一对象的场景中非常有用,比如配置管理、日志记录、数据库连接池等。

本文将通过一个简单的 Go 示例,带你理解单例模式的基本概念和实现方法。即使你是设计模式的新手,也能轻松掌握!


什么是单例模式?

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。它的主要特点包括:

  1. 唯一性:整个程序中只有一个实例存在。
  2. 全局访问:通过一个静态方法或变量来访问该实例。

单例模式的核心思想是通过控制类的实例化过程,避免外部代码随意创建多个实例。


为什么需要单例模式?

在某些场景中,我们需要确保一个类只有一个实例。例如:

  • 配置管理:程序的配置信息只需要加载一次,全局共享。
  • 日志记录:日志系统只需要一个实例来记录所有日志。
  • 数据库连接池:数据库连接池只需要一个实例来管理所有连接。

如果这些场景中允许多个实例存在,可能会导致资源浪费或数据不一致的问题。

Go实现单例模式

先从简单的单例模式入手,通过简单的锁机制实现单例

package main

import (
    "fmt"
    "sync"
)

// 定义单例结构体
type Singleton struct{}

// 全局变量,用于存储单例实例
var instance *Singleton
// 同步对象,确保单例实例只被创建一次
var once sync.Once

// 获取单例实例的函数
func GetInstance() *Singleton {
    // 使用 once.Do 确保单例实例只被创建一次
    once.Do(func() {
        // 创建单例实例
        instance = &Singleton{}
    })
    // 返回单例实例
    return instance
}

// 方法,用于打印单例实例的内存地址
func (s *Singleton) PrintAddress() {
    fmt.Println(s)
}

func main() {
    // 获取单例实例
    singleton1 := GetInstance()
    // 打印单例实例的内存地址
    singleton1.PrintAddress()
    // 再次获取单例实例
    singleton2 := GetInstance()
    // 打印单例实例的内存地址
    singleton2.PrintAddress()
    // 打印单例实例的内存地址
    fmt.Println("Address:", singleton1)
    // 打印单例实例的内存地址
    fmt.Println("Address:", singleton2)
}
    

通过这个例子,你会发现singleton1和singleton2的地址相同

如果我们想通过单例模式来创建其他类实例,需要引入模板,参考下列代码。

假设我们需要创建一个Redis连接池,通过单例模式实现可以确保一个实例管理所有链接

package main

import (
    "fmt"
    "sync"
)

// RedisConPool 结构体,实现单例模式
type RedisConPool struct{}

// 单例实例
var instance *RedisConPool
// 用于保证线程安全的互斥锁
var once sync.Once

// GetInstance 函数,获取单例实例
func GetInstance() *RedisConPool {
    once.Do(func() {
        // 只执行一次,创建单例实例
        instance = &RedisConPool{}
        fmt.Println("RedisConPool instance created!")
    })
    return instance
}

// PrintAddress 方法,打印实例地址
func (r *RedisConPool) PrintAddress() {
    fmt.Printf("%p\n", r)
}

func main() {
    // 获取单例实例
    redis1 := GetInstance()
    redis2 := GetInstance()
    redis1.PrintAddress()
    redis2.PrintAddress()
    // 比较两个单例实例
    fmt.Println("redis1 == redis2 ?", redis1 == redis2)
}

相关文章:

  • SAP Commerce(Hybris)营销模块(一):商城产品折扣配置
  • Android LeakCanary 使用 · 原理详解
  • Centos7阿里云yum源
  • Go语言入门基础详解
  • 使用docker部署宝塔环境
  • c#实现添加和删除Windows系统环境变量
  • 本地知识库RAG总结
  • Elasticsearch:语义文本 - 更简单、更好、更精炼、更强大 8.18
  • 使用mvn archetype命令,构建自定义springboot archetype脚手架创建工程的方法
  • 2.JVM-通俗易懂理解类加载过程
  • Java vs Go:SaaS 系统架构选型解析与最佳实践
  • 【FreeRTOS】FreeRTOS操作系统在嵌入式单片机上裸机移植
  • CSS 的 inherit、initial、revert 和 unset区别
  • mysql安装与使用
  • 深入理解 ALSA 声卡驱动:从理论到实践,解决嵌入式 Linux 声卡无声问题
  • 辉芒单片机FLASH->ACR导致的问题
  • Springboot+Netty+WebSocket搭建简单的消息通知
  • 大一新生备战蓝桥杯c/c++B组——2024年省赛真题解题+心得分享
  • 数仓建模和标签体系之间存在着密切的依赖关系
  • 27_Java2DRenderer结合freemarker动态生成图片
  • 济南市委副秘书长吕英伟已任历下区领导
  • 美国政府信用卡被设1美元限额,10美元采购花一两小时填表
  • 言短意长|西湖大学首次“走出西湖”
  • 中方发布会:中美经贸高层会谈取得了实质性进展,达成了重要共识
  • 韩国前国务总理韩德洙加入国民力量党
  • 央行:中国政府债务扩张仍有可持续性