在Go语言中的单例模式

huangapple go评论81阅读模式
英文:

Singleton in go

问题

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

英文:

How does one implement the Singleton design pattern in the go programming language?

答案1

得分: 65

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

package singleton

type single struct {
        O interface{};
}

var instantiated *single = nil

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

singleinstantiated 是私有的,但 New() 是公开的。因此,你不能直接实例化 single 而必须通过 New(),并且它使用私有布尔值 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 的建议,将一个包视为单例。最后,请考虑其他人的建议:单例通常是问题实现的指标。

英文:

Setting aside the argument of whether or not implementing the singleton pattern is a good idea, here's a possible implementation:

package singleton

type single struct {
        O interface{};
}

var instantiated *single = nil

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

single and instantiated are private, but New() is public. Thus, you can't directly instantiate single without going through New(), and it tracks the number of instantiations with the private boolean instantiated. Adjust the definition of single to taste.

However, as several others have noted, this is not thread-safe, unless you're only initializing your singleton in init(). A better approach would be to leverage sync.Once to do the hard work for you:

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
}

See also, hasan j's suggestion of just thinking of a package as a singleton. And finally, do consider what others are suggesting: that singletons are often an indicator of a problematic implementation.

答案2

得分: 20

最好的方法是:

 package singleton

 import "sync"

 type singleton struct {
 }

 var instance *singleton
 var once sync.Once

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

你应该阅读这个链接

英文:

The best approach will be:

 package singleton

 import "sync"

 type singleton struct {
 }

 var instance *singleton
 var once sync.Once

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

You should read this Link

答案3

得分: 10

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

还可以参考类似的问题:如何在Python中创建单例模式

英文:

Just put your variables and functions at the package level.

Also see similar question: How to make a singleton in Python

答案4

得分: 9

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

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

我会遵循@marketer的建议,使用"sync"包:

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 
}
英文:

I think that in a concurrent world we need to be a bit more aware that these lines are not executed atomically:

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

I would follow the suggestion of @marketer and use the package "sync"

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 
}

答案5

得分: 6

在试图找到一种弯曲Go语言以适应你的意愿之前,你可能想看一些文章:

总结起来,随着时间的推移,人们发现单例模式并不是最佳选择,而且在我看来,尤其是如果你想进行任何测试驱动开发:在许多层面上,它们几乎和全局变量一样糟糕。

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

英文:

Before trying to find a way to bend Go to your will, you might want to take a look at some articles:

In summary, over time people have found singletons to be less than optimal, and imho especially if you are trying to do any test-driven development: on many levels they are pretty much as bad as global variables.

[disclaimer: I know its not a strict answer to your question but it really is relevant]

答案6

得分: 4

简单易懂,如下所示的代码:

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

因为您正在访问相同的资源。这就是为什么我添加了互斥锁以确保从多个进程中并发访问count属性的原因。:-)

来源:

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

英文:

Easy peasy as you can see in the following code:

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())
}

Expected result would be:

> 1 2 3

Because you're accessing to the same resource. That's the reason I've added mutex in order to be concurrent proven accessing count attribute from multiple processes. 在Go语言中的单例模式

Source:

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

答案7

得分: 3

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

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

英文:

You can do initialization using the once package:

This will ensure that your init methods only get called once.

答案8

得分: 1

只需拥有一个静态、最终、常量、全局的应用程序范围内的对象实例即可。

然而,这与面向对象的范例相矛盾。它的使用应限于原始类型和不可变对象,而不是可变对象。

英文:

Just have a single static, final, constant, global, application-wide instance of the Object you want.

This however contradicts the OO paradigm. Its use should be limited to primitives and immutable objects, not to mutable objects.

答案9

得分: 1

你应该知道Once.Do非常重视只执行一次的代码。这意味着,无论代码是否发生恐慌,代码都只会执行一次:

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

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

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

英文:

You should be aware that Once.Do is serious about executing the code only once. That means, the code is always executed only once, even though it might have panicked:

from https://golang.org/pkg/sync/#Once.Do

> If f (note: the once logic) panics, Do considers it to have returned; future calls of Do return without calling f.

I used mutexes instead to ensure unique initialisation of a global configuration variable to overcome this restriction:

答案10

得分: 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中实现单例模式,但我认为这些答案从必须完全复制该模式的前提出发,而不是从解决Go中实际问题的角度出发。

英文:

I think if you want something like a singleton in Go you should try and think about what you're trying to achieve. Unlike Java or C#, Go has no concept of "static" classes, so the traditional distinction is less clear. In my mind, the key point you'd want from a singleton is not allowing others to construct it. To that end, I think it can achieved more simply: export an interface, implement an unexported class which implements this interface, and export a single instance of it. For instance:

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)

As the other answers show, there are many more literal ways you can implement the singleton pattern in Go, but they strike me as answers that start from the premise that the pattern must be copied exactly, rather than starting from solving a real problem you have in Go.

huangapple
  • 本文由 发表于 2009年12月1日 08:12:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/1823286.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定