59

在 Go 编程语言中如何实现单例设计模式?

4

10 回答 10

57

撇开单例模式是否是一个好主意的争论,这里有一个可能的实现:

package singleton

type single struct {
        O interface{};
}

var instantiated *single = nil

func New() *single {
        if instantiated == nil {
                instantiated = new(single);
        }
        return instantiated;
}

single并且instantiated是私有的,但是New()是公共的。因此,您不能在不single通过的情况下直接实例化New(),它会使用私有 boolean 跟踪实例化的数量instantiated。调整single口味的定义。

但是,正如其他几个人所指出的那样,这不是线程安全的,除非您只是在init(). 更好的方法是利用sync.Once为您完成艰苦的工作:

package singleton

import "sync"

type single struct {
        O interface{};
}

var instantiated *single
var once sync.Once

func New() *single {
        once.Do(func() {
                instantiated = &single{}
        })
        return instantiated
}

另请参阅 hasan j 的建议,即仅将包视为单例。最后,请考虑其他人的建议:单例通常是有问题的实现的指标。

于 2009-12-02T21:50:32.830 回答
13

最好的方法是:

 package singleton

 import "sync"

 type singleton struct {
 }

 var instance *singleton
 var once sync.Once

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

你应该阅读这个链接

于 2015-10-29T09:55:53.517 回答
9

只需将您的变量和函数放在包级别。

另见类似问题:如何在 Python 中制作单例

于 2009-12-01T00:34:43.837 回答
9

我认为在并发世界中,我们需要更加意识到这些行不是原子执行的:

if instantiated == nil {
    instantiated = new(single);
}

我会遵循@marketer 的建议并使用包“同步”

import "sync"

type MySingleton struct {

}

var _init_ctx sync.Once 
var _instance *MySingleton

func New() * MySingleton {
     _init_ctx.Do( func () { _instance = new(MySingleton) }  )
     return _instance 
}
于 2012-11-19T18:00:23.857 回答
6

在试图找到一种方法来屈服于你的意志之前,你可能想看看一些文章:

总而言之,随着时间的推移,人们发现单例并不是最优的,恕我直言,尤其是如果您尝试进行任何测试驱动的开发:在许多层面上,它们几乎与全局变量一样糟糕。

[免责声明:我知道这不是对您问题的严格回答,但确实是相关的]

于 2009-12-01T00:31:53.563 回答
4

您可以在以下代码中看到简单的 peasy:

package main

import (
    "fmt"
    "sync"
)

type singleton struct {
    count int
    sync.RWMutex
}

var instance singleton 

func GetInstance() *singleton {
    return &instance 
}

func (s *singleton) AddOne() {
    s.Lock()
    defer s.Unlock()
    s.count++
}

func (s *singleton) GetCount() int {
    s.RLock()
    defer s.RUnlock()
    return s.count
}

func main() {
    obj1 := GetInstance()
    obj1.AddOne()
    fmt.Println(obj1.GetCount())
    obj2 := GetInstance()
    obj2.AddOne()
    fmt.Println(obj2.GetCount())    
    obj3 := GetInstance()
    obj3.AddOne()
    fmt.Println(obj3.GetCount())
}

预期结果将是:

1 2 3

因为您正在访问相同的资源。这就是我添加互斥锁以同时证明从多个进程访问计数属性的原因。:-)

来源:

https://play.golang.org/p/2XnLztX8Gs5

于 2019-08-01T09:18:23.687 回答
3

您可以使用once 包进行初始化:

这将确保您的 init 方法只被调用一次。

于 2009-12-01T01:35:10.470 回答
1

只需拥有您想要的 Object 的单个静态、最终、常量、全局、应用程序范围的实例。

然而,这与 OO 范式相矛盾。它的使用应仅限于基元和不可变对象,而不是可变对象。

于 2009-12-01T00:42:21.483 回答
1

您应该意识到Once.Do只执行一次代码是很严肃的。这意味着,代码总是只执行一次,即使它可能已经恐慌:

来自https://golang.org/pkg/sync/#Once.Do

如果 f(注意:once 逻辑)发生恐慌,Do 认为它已经返回;Do 的未来调用返回而不调用 f。

我使用互斥锁来确保全局配置变量的唯一初始化来克服这个限制:

于 2016-01-07T19:00:03.847 回答
0

我认为如果你想要像 Go 中的单例这样的东西,你应该试着想想你想要实现的目标。与 Java 或 C# 不同,Go 没有“静态”类的概念,因此传统的区别不太清楚。在我看来,您希望单例的关键点是不允许其他人构建它。为此,我认为它可以更简单地实现:导出一个接口,实现一个未导出的实现该接口的类,并导出它的单个实例。例如:

var DefaultTransformer Transformer = &transformer{}

type Transformer interface {
    Transform(s string) string
}

type transformer struct {
    
}

func (t *transformer) Transform(s string) string {
    return s + "1"
}

var _ Transformer = (*transformer)(nil)

正如其他答案所示,您可以在 Go 中实现单例模式的更多文字方法,但它们让我印象深刻,因为它们的答案是从必须准确复制模式的前提开始的,而不是从解决您遇到的实际问题开始在围棋。

于 2021-05-15T17:15:05.160 回答