在 Go 编程语言中如何实现单例设计模式?
10 回答
撇开单例模式是否是一个好主意的争论,这里有一个可能的实现:
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 的建议,即仅将包视为单例。最后,请考虑其他人的建议:单例通常是有问题的实现的指标。
最好的方法是:
package singleton
import "sync"
type singleton struct {
}
var instance *singleton
var once sync.Once
func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}
你应该阅读这个链接
只需将您的变量和函数放在包级别。
另见类似问题:如何在 Python 中制作单例
我认为在并发世界中,我们需要更加意识到这些行不是原子执行的:
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
}
在试图找到一种方法来屈服于你的意志之前,你可能想看看一些文章:
总而言之,随着时间的推移,人们发现单例并不是最优的,恕我直言,尤其是如果您尝试进行任何测试驱动的开发:在许多层面上,它们几乎与全局变量一样糟糕。
[免责声明:我知道这不是对您问题的严格回答,但确实是相关的]
您可以在以下代码中看到简单的 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
因为您正在访问相同的资源。这就是我添加互斥锁以同时证明从多个进程访问计数属性的原因。:-)
来源:
您可以使用once 包进行初始化:
这将确保您的 init 方法只被调用一次。
只需拥有您想要的 Object 的单个静态、最终、常量、全局、应用程序范围的实例。
然而,这与 OO 范式相矛盾。它的使用应仅限于基元和不可变对象,而不是可变对象。
您应该意识到Once.Do
只执行一次代码是很严肃的。这意味着,代码总是只执行一次,即使它可能已经恐慌:
来自https://golang.org/pkg/sync/#Once.Do
如果 f(注意:once 逻辑)发生恐慌,Do 认为它已经返回;Do 的未来调用返回而不调用 f。
我使用互斥锁来确保全局配置变量的唯一初始化来克服这个限制:
我认为如果你想要像 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 中实现单例模式的更多文字方法,但它们让我印象深刻,因为它们的答案是从必须准确复制模式的前提开始的,而不是从解决您遇到的实际问题开始在围棋。