发布日期

GoLang 设计模式之单例模式 (Singleton Pattern)

软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设 计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。

💍 单例模式 (Singleton Pattern)

现实世界的例子:一次只能有一个国家的总统。 简单来说:确保只创建特定类的一个对象。 单例模式实际上被认为是反模式,应该避免过度使用它。它不一定是坏的,可能有一些有效的用例,但应谨慎使用,因为它在您的应用程序中引入了一个全局状态,并且在一个地方更改它可能会影响其他区域,并且它可能变得非常难以调试。关于它们的另一个坏处是它使你的代码紧密耦合加上嘲弄单例可能很困难。

代码实现 (源码

package examples

import (
    "sync"
)

// President 构建一个示例结构体
type President struct {
    Name string
}

// 设置一个私有变量作为每次要返回的单例
var once sync.Once
var instance1 *President

// GetInstanceThreadUnsafe 写一个可以获取单例的方法(懒汉方式)
// 存在线程安全问题,高并发时有可能创建多个对象
func GetInstanceThreadUnsafe(name string) *President {
    if instance1 == nil {
        instance1 = &President{Name: name}
    }
    return instance1
}

// 线程安全
var instance2 *President

// GetInstanceThreadSafety 写一个可以获取单例的方法(避免了每次加锁,提高代码效率)
func GetInstanceThreadSafety(name string) *President {
    once.Do(func() {
        instance2 = &President{Name: name}
    })
    return instance2
}

测试用例(源码

package examples

import (
    "fmt"
    "strconv"
    "sync"
    "testing"
)

// command: go test -v singleton_test.go singleton.go
func TestSingleton(t *testing.T) {
    t.Run("线程非安全测试", testGetInstanceThreadUnsafe)
    t.Run("线程安全测试", testGetInstanceThreadSafety)
}

// 单线程测试
func testGetInstanceThreadUnsafe(t *testing.T) {
    // 普通测试
    instance1 := GetInstanceThreadUnsafe("yxx")
    instance2 := GetInstanceThreadUnsafe("张三")
    // 如果单列是一个指针的值
    if fmt.Sprintf("%p", instance1) != fmt.Sprintf("%p", instance2) {
        t.Fail()
    }
}

// 多线程测试
func testGetInstanceThreadSafety(t *testing.T) {

    var wg sync.WaitGroup
    presidentChan := make(chan *President, 1000)
    // 模拟 1000 个并发获取单例
    for i := 0; i < 1000; i++ {
        name := "名称" + strconv.Itoa(i)
        wg.Add(1)
        go func() {
            defer wg.Done()
            instance := GetInstanceThreadSafety(name)
            presidentChan <- instance
        }()

    }
    wg.Wait()

    close(presidentChan)

    temp := make(map[*President]*President)

    for v := range presidentChan {
        temp[v] = v
    }

    // 判断是否有重复的
    t.Logf("获取了 %d 个实例", len(temp))

    if len(temp) > 1 {
        t.Fail()
    }

}

注意

  • 设计模式不是解决所有问题的灵丹妙药。

  • 不要试图强迫他们; 如果这样做的话,应该发生坏事。

  • 请记住,设计模式是问题的解决方案,而不是解决问题的解决方案;所以不要过分思考。

  • 如果以正确的方式在正确的地方使用,他们可以证明是救世主; 否则他们可能会导致代码混乱。

声明

Amath THIAM 4个月前

test

备案号:湘ICP备2020019075号 © 2020 yxx All rights reserved. | my github