如何在go编程语言中实现Singleton设计模式?
答案 0 :(得分:37)
抛开关于是否实现单例模式的论点是一个好主意,这是一个可能的实现:
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()
,并且它会使用私有布尔值instantiated
跟踪实例化的数量。调整single
的定义以品尝。
但是,正如其他几个人noted一样,这不是线程安全的,除非你只是在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建议将包视为单身。最后,请考虑其他人的建议:单身人士往往是有问题的实施的指标。
答案 1 :(得分:11)
在尝试找到弯曲的方法之前,你可能想看看一些文章:
总而言之,随着时间的推移,人们发现单身人士不是最优的,如果你想尝试任何以测试为导向的开发,那么他们就会非常:在许多层面上他们都差不多全局变量。
[免责声明:我知道这不是你问题的严格答案,但它确实是相关的]
答案 2 :(得分:9)
只需将变量和函数放在包级别。
答案 3 :(得分:8)
我认为在并发的世界中,我们需要更多地意识到这些行不是以原子方式执行的:
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
}
答案 4 :(得分:5)
最好的方法是:
package singleton
import "sync"
type singleton struct {
}
var instance *singleton
var once sync.Once
func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}
您应该阅读此Link
答案 5 :(得分:3)
您可以使用once package:
进行初始化这将确保只调用一次init方法。
答案 6 :(得分:1)
只需要一个所需对象的静态,最终,常量,全局,应用程序范围的实例。
然而,这与OO范式相矛盾。它的使用应限于基元和不可变对象,而不是可变对象。答案 7 :(得分:1)
您应该知道Once.Do
对于仅执行一次代码非常认真。这意味着代码总是只执行一次,即使它可能有恐慌:
来自https://golang.org/pkg/sync/#Once.Do
如果f(注意:曾经的逻辑)恐慌,请考虑它已经返回;将来调用Do而不调用f。
我使用互斥锁来确保全局配置变量的唯一初始化以克服此限制:
答案 8 :(得分:1)
在下面的代码中可以轻松看到
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 属性的原因。 :-)
来源:
答案 9 :(得分: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 中遇到的问题。