go - 当用 * 实例化 var 时,单例测试不起作用

标签 go

我正在遵循本书 (https://github.com/PacktPublishing/Go-Design-Patterns/blob/master/Chapter02/main.go) 中描述的单例设计模式,并且我在文件“singleton2.go”中有以下代码:

package singleton2

type Singleton interface {
  AddOne() int
}

type singleton struct {
  count int
}

//var instance = &singleton{}
var instance *singleton

func GetInstance() *singleton {
  if instance == nil {
    return new(singleton)
  }
  return instance
}

func (s *singleton) AddOne() int {
  s.count++
  return s.count
}

然后我有这个测试文件(singleton2_test.go):

package singleton2

import "testing"

func TestGetInstance(t *testing.T) {
  counter1 := GetInstance()
  if counter1 == nil {
    t.Fatal("expected pointer to Singleton after calling GetInstance(), not nil")
  }

  expectedCounter := counter1

  currentCount := counter1.AddOne()
  if currentCount != 1 {
    t.Errorf("After calling for the first time to count, the counter must be 1 but it is %d", currentCount)
  }

  counter2 := GetInstance()
  if counter2 != expectedCounter {
    t.Errorf("Expected same instance in counter2 but it got a different instance.")
    t.Logf("Got %v, want %v", counter2, expectedCounter)
  }

  currentCount = counter2.AddOne()
  if currentCount != 2 {
    t.Errorf("After calling AddOne() using second counter, the count must be 2, but it was %d", currentCount)
  }

}

问题是测试总是失败:

--- FAIL: TestGetInstance (0.00s)
    singleton2_test.go:20: Expected same instance in counter2 but it got a different instance.
    singleton2_test.go:21: Got &{0}, want &{1}
    singleton2_test.go:26: After calling AddOne() using second counter, the count must be 2, but it was 1
FAIL
exit status 1
FAIL    github.com/d3c3/design_patterns/singleton/singleton2    0.029s

有趣的是,如果我将此行 var instance *singleton 更改为此行 var instance = &singleton{} 测试通过!?这是为什么? IMO,它也应该与“var instance *singleton”一起使用

谁能解释这种行为差异?

最佳答案

遵循代码的逻辑,很明显问题出在哪里。

当您声明但不初始化单例 (var instance *singleton) 时,instancenil。调用 GetInstance 会将 instance == nil 评估为 true 并在每次调用时返回一个新的 *singleton。这就是为什么 counter2 永远不会等于 expectedCounter - 每次调用 GetInstance 都会返回一个新的计数器实例。

当您初始化单例实例 (var instance = &singleton{}) 时,调用 GetInstance 将返回该单例实例,因为它不是nil.

我想您可能希望将 GetInstance 修改为如下内容:

func GetInstance() *singleton {
    if instance == nil {
        instance = new(singleton)
    }
    return instance
}

编辑

虽然你没有提到你需要这个单例是线程安全的,用户colminator正确地指出,以这种方式实例化一个单例可能会导致多个 go-routines 实例化它们自己的单例实例,从而破坏了拥有一个单例的目的。鉴于 Golang 中的并发性如此之高,了解更多可能会有所帮助! Check out the link he posted here .

关于go - 当用 * 实例化 var 时,单例测试不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56534491/

相关文章:

json - 使用 golang 将一个简单的 json 文件输出到 rest api

go - 迭代边界与数据类型相同

go - 如何使用for循环创建对象 slice

go - 无法生成覆盖文件

go - 为什么是 `*invalid type` 而不是 `*Games` ?

reflection - 通过反射传递引用嵌套结构

go - 从另一个文件访问在 main func 中创建的运行时配置实例

go - 如何更改导入文件?

go - 使用 Go 控制另一个窗口

bash - 在Circleci中使用golang-ci-lint