”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 单例设计模式

单例设计模式

发布于2024-07-30
浏览:214

Singleton Design Pattern

单例设计模式是软件编程中最重要和最常用的设计模式之一。它确保类在应用程序运行时只有一个实例,并提供对该实例的全局访问点。在这篇文章中,我们将讨论 Singleton 的重要性,如何在 Golang 中实现它,以及它带来的好处,特别是在并发环境中。

什么是单例?

Singleton 是一种将类的实例限制为单个实例的设计模式。它在需要单点控制或单个共享资源的情况下特别有用,例如:

  • 配置管理器,需要集中应用程序设置。
  • 数据库连接池,必须有效管理有限数量的连接。
  • 记录器,其中日志一致性至关重要。

为什么使用单例?

我将列出一些关于 Pattern 实现的更有意义的观点,同时也表明并非一切都是美好的,我们可能会遇到一些问题。

好处

  • 全局一致性:保证应用程序的所有点都使用相同的实例,提供数据和行为的一致性。
  • 访问控制:集中控制实例的创建和访问,方便对象生命周期的维护和管理。
  • 资源效率:避免不必要的多个实例创建,节省内存和处理资源。

缺点

  • 测试难度:单例会使编写单元测试变得更加困难,因为它们引入了需要管理的全局状态。
  • 增加耦合:过度使用单例会导致组件之间的耦合更紧密,从而难以维护和发展应用程序。

实现单例

为了实现单例,我将使用 Golang。在这种语言中,我们必须特别注意并发性,以确保只创建一个实例,即使多个 goroutine 尝试同时访问该实例也是如此。

为了使我们的示例更接近现实世界,让我们为我们的应用程序创建一个记录器。记录器是应用程序中的常用工具,需要唯一以确保日志一致性。

1 - 定义结构

首先,我们定义我们想要拥有单个实例的结构。

package logger

import (
    "fmt"
    "sync"
)

type Logger struct {}

var loggerInstance *Logger

2 - 实现NewInstance函数

NewInstance函数负责返回Singleton结构的单个实例。我们使用互斥体来确保并发环境中的安全性,实现双重检查锁定以提高效率。

package logger

import (
    "fmt"
    "sync"
)

type Logger struct{}

var logger *Logger
var mtx = &sync.Mutex{}

func NewInstance() *Logger {
    if logger == nil {
        mtx.Lock()
        defer mtx.Unlock()
        if logger == nil {
            fmt.Println("Creating new Logger")
            logger = &Logger{}
        }
    } else {
        fmt.Println("Logger already created")
    }
    return logger
}

3 - 实现日志类型

日志工具总是有一些日志类型,例如Info仅显示信息,Error显示错误等等。这也是过滤我们想要在应用程序中显示的信息类型的一种方法。

因此,让我们创建一个方法来显示 Info 类型的日志。为此,我们将创建一个函数来接收日志消息并将其格式化为 INFO 格式。

package logger

import (
    "fmt"
    "sync"
    "time"
)

const (
    INFO    string = "INFO"
)

type Logger struct{}

var logger *Logger
var mtx = &sync.Mutex{}

func NewInstance() *Logger {
    if logger == nil {
        mtx.Lock()
        defer mtx.Unlock()
        if logger == nil {
            fmt.Println("Creating new logger")
            logger = &Logger{}
        }
    } else {
        fmt.Println("Logger already created")
    }
    return logger
}

func (l *Logger) Info(message string) {
    fmt.Printf("%s - %s: %s\n", time.Now().UTC().Format(time.RFC3339Nano), INFO, message)
}

4 - 使用记录器

要使用我们的新记录器,我们将在主包中实例化它并创建一个日志以查看此实现的工作原理。

package main

import (
    "playground-go/pkg/logger"
)

func main() {
    log := logger.NewInstance()
    log.Info("This is an example of log")
}

这是我们运行程序的结果:

Creating new logger
2024-07-03T19:34:57.609599Z - INFO: This is an example of log

如果我们想测试NewInstance是否真的保证只有一个实例在运行,我们可以进行以下测试。

package main

import (
    "fmt"
    "playground-go/pkg/logger"
)

func main() {
    log := logger.NewInstance()
    log.Info("This is an example of log")

    log2 := logger.NewInstance()
    log2.Info("This is another example of log")

    if log == log2 {
        fmt.Println("same instance")
    } else {
        fmt.Println("different instance")
    }
}

我们的日志已更改,现在我们可以看到我们阻止了新实例的创建:

Creating new logger
2024-07-03T19:45:19.603783Z - INFO: This is an example of log
Logger already created
2024-07-03T19:45:19.603793Z - INFO: This is another example of log
same instance

结论

单例模式是一种强大的工具,可确保应用程序运行时仅存在特定类的一个实例。在记录器示例中,我们了解了如何应用此模式来确保整个应用程序中的日志一致性。

希望这可以帮助您更好地理解 Golang 中的 Singleton。

版本声明 本文转载于:https://dev.to/rflpazini/singleton-design-pattern-1n51?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • Java开发者如何保护数据库凭证免受反编译?
    Java开发者如何保护数据库凭证免受反编译?
    在java 在单独的配置文件保护数据库凭证的最有效方法中存储凭据是将它们存储在单独的配置文件中。该文件可以在运行时加载,从而使登录数据从编译的二进制文件中远离。使用prevereness class import java.util.prefs.preferences; 公共类示例{ 首选项...
    编程 发布于2025-04-18
  • 为什么PYTZ最初显示出意外的时区偏移?
    为什么PYTZ最初显示出意外的时区偏移?
    与pytz 最初从pytz获得特定的偏移。例如,亚洲/hong_kong最初显示一个七个小时37分钟的偏移: 差异源利用本地化将时区分配给日期,使用了适当的时区名称和偏移量。但是,直接使用DateTime构造器分配时区不允许进行正确的调整。 example pytz.timezone(...
    编程 发布于2025-04-18
  • Java中Lambda表达式为何需要“final”或“有效final”变量?
    Java中Lambda表达式为何需要“final”或“有效final”变量?
    Lambda Expressions Require "Final" or "Effectively Final" VariablesThe error message "Variable used in lambda expression shou...
    编程 发布于2025-04-18
  • 如何克服PHP的功能重新定义限制?
    如何克服PHP的功能重新定义限制?
    克服PHP的函数重新定义限制在PHP中,多次定义一个相同名称的函数是一个no-no。尝试这样做,如提供的代码段所示,将导致可怕的“不能重新列出”错误。 但是,PHP工具腰带中有一个隐藏的宝石:runkit扩展。它使您能够灵活地重新定义函数。 runkit_function_renction_re...
    编程 发布于2025-04-18
  • JavaScript中如何动态访问全局变量?
    JavaScript中如何动态访问全局变量?
    在JavaScript 一种方法是使用窗口对象存储和检索变量。通过引用全局范围,可以使用其名称动态访问变量。 //一个脚本 var somevarname_10 = 20; //另一个脚本 window.all_vars = {}; window.all_vars ['somevarnam...
    编程 发布于2025-04-18
  • 如何高效地在一个事务中插入数据到多个MySQL表?
    如何高效地在一个事务中插入数据到多个MySQL表?
    mySQL插入到多个表中,该数据可能会产生意外的结果。虽然似乎有多个查询可以解决问题,但将从用户表的自动信息ID与配置文件表的手动用户ID相关联提出了挑战。使用Transactions和last_insert_id() 插入用户(用户名,密码)值('test','test...
    编程 发布于2025-04-18
  • 找到最大计数时,如何解决mySQL中的“组函数\”错误的“无效使用”?
    找到最大计数时,如何解决mySQL中的“组函数\”错误的“无效使用”?
    如何在mySQL中使用mySql 检索最大计数,您可能会遇到一个问题,您可能会在尝试使用以下命令:理解错误正确找到由名称列分组的值的最大计数,请使用以下修改后的查询: 计数(*)为c 来自EMP1 按名称组 c desc订购 限制1 查询说明 select语句提取名称列和每个名称...
    编程 发布于2025-04-18
  • JavaScript计算两个日期之间天数的方法
    JavaScript计算两个日期之间天数的方法
    How to Calculate the Difference Between Dates in JavascriptAs you attempt to determine the difference between two dates in Javascript, consider this s...
    编程 发布于2025-04-18
  • CSS强类型语言解析
    CSS强类型语言解析
    您可以通过其强度或弱输入的方式对编程语言进行分类的方式之一。在这里,“键入”意味着是否在编译时已知变量。一个例子是一个场景,将整数(1)添加到包含整数(“ 1”)的字符串: result = 1 "1";包含整数的字符串可能是由带有许多运动部件的复杂逻辑套件无意间生成的。它也可以是故意从单个真理...
    编程 发布于2025-04-18
  • 如何使用node-mysql在单个查询中执行多个SQL语句?
    如何使用node-mysql在单个查询中执行多个SQL语句?
    Multi-Statement Query Support in Node-MySQLIn Node.js, the question arises when executing multiple SQL statements in a single query using the node-mys...
    编程 发布于2025-04-18
  • 为什么HTML无法打印页码及解决方案
    为什么HTML无法打印页码及解决方案
    无法在html页面上打印页码? @page规则在@Media内部和外部都无济于事。 HTML:Customization:@page { margin: 10%; @top-center { font-family: sans-serif; font-weight: bo...
    编程 发布于2025-04-18
  • 哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    在Python Matplotlib's path.contains_points FunctionMatplotlib's path.contains_points function employs a path object to represent the polygon.它...
    编程 发布于2025-04-18
  • 为什么我会收到MySQL错误#1089:错误的前缀密钥?
    为什么我会收到MySQL错误#1089:错误的前缀密钥?
    mySQL错误#1089:错误的前缀键错误descript [#1089-不正确的前缀键在尝试在表中创建一个prefix键时会出现。前缀键旨在索引字符串列的特定前缀长度长度,可以更快地搜索这些前缀。了解prefix keys `这将在整个Movie_ID列上创建标准主键。主密钥对于唯一识别...
    编程 发布于2025-04-18
  • 对象拟合:IE和Edge中的封面失败,如何修复?
    对象拟合:IE和Edge中的封面失败,如何修复?
    To resolve this issue, we employ a clever CSS solution that solves the problem:position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%)...
    编程 发布于2025-04-18
  • 使用jQuery如何有效修改":after"伪元素的CSS属性?
    使用jQuery如何有效修改":after"伪元素的CSS属性?
    在jquery中了解伪元素的限制:访问“ selector 尝试修改“:”选择器的CSS属性时,您可能会遇到困难。 This is because pseudo-elements are not part of the DOM (Document Object Model) and are th...
    编程 发布于2025-04-18

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3