手撸golang 创建型设计模式 单例模式

手撸golang 创建型设计模式 单例模式

缘起

最近复习设计模式
拜读谭勇德的<<设计模式就该这样学>>
本系列笔记拟采用golang练习之

单例模式

单例模式(Singleton Pattern)指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点,属于创建型设计模式。
_

三种常见单例模式

  • 饿汉式单例

    • 类/模块初始化时立即创建的静态全局单例
  • 双重检查单例

    • 提供获取单例的方法
    • 在该方法中通过双重检查静态实例是否为null, 以延迟初始化全局单例
    • 首次检查不加同步锁, 二次检查加同步锁, 以保证单例只创建一次
  • 容器式单例

    • 存在全局唯一的Bean容器
    • 通过Bean的名称读取或设置与该名称绑定的bean单例
    • 通过读写锁控制并发安全
    • 常用于需要持有大量单例的场景, 如IOC容器

单元测试

singleton_test.go, 依次调用了三种单例的方法

package patterns

import "testing"
import (sg "learning/gooop/creational_patterns/singleton")

func Test_Singleton(t *testing.T) {
    fnTestSingleton := func(it sg.IDemoSingleton) {
        it.Hello()
    }

    fnTestSingleton(sg.HungrySingleton)
    fnTestSingleton(sg.GetDualCheckSingleton())

    ok, it := sg.DefaultBeanContaibner.GetBean("IDemoSingleton")
    if ok {
        fnTestSingleton(it.(sg.IDemoSingleton))
    }
}

测试输出

$ go test -v singleton_test.go 
=== RUN   Test_Singleton
tHungrySingleton.Hello
tDualCheckSingleton.Hello
tContainedSingleton.Hello
--- PASS: Test_Singleton (0.00s)
PASS
ok      command-line-arguments  0.002s

IDemoSingleton.go

定义单例的接口

package singleton

type IDemoSingleton interface {
    Hello()
}

饿汉式单例

HungrySingleton.go演示如何实现一个饿汉式单例

package singleton

import (
    "fmt"
)

type tHungrySingleton struct {}

func newHungrySingleton() *tHungrySingleton {
    return &tHungrySingleton{}
}

func (me *tHungrySingleton) Hello() {
    fmt.Printf("tHungrySingleton.Hello\n")
}

var HungrySingleton IDemoSingleton = newHungrySingleton()

双重检查单例

DualCheckSingleton.go演示如何创建一个双重检查单例

package singleton

import (
    "fmt"
    "sync"
)

type tDualCheckSingleton struct {}


func newDualCheckSingleton() *tDualCheckSingleton {
    return &tDualCheckSingleton{}
}


func (me *tDualCheckSingleton) Hello() {
    fmt.Printf("tDualCheckSingleton.Hello\n")
}

var gDualCheckSingleton IDemoSingleton = nil
var gSingletonLock = new(sync.Mutex)

func GetDualCheckSingleton() IDemoSingleton {
    if gDualCheckSingleton == nil {
        gSingletonLock.Lock()
        defer gSingletonLock.Unlock()

        if gDualCheckSingleton == nil {
            gDualCheckSingleton = newDualCheckSingleton()
        }
    }

    return gDualCheckSingleton
}

容器式单例

ContainedSingleton.go演示如何通过Bean容器持有大量Bean单例. Bean容器本身是一个饿汉式单例.

package singleton

import (
    "errors"
    "fmt"
    "sync"
)

type IBeanContainer interface {
    GetBean(string) (bool, interface{})
    SetBean(string, interface{}) error
}

type tBeanContainer struct {
    mBeans map[string]interface{}
    mRWMutex *sync.RWMutex
}

func newBeanContainer() *tBeanContainer {
    return &tBeanContainer{
        mBeans: make(map[string]interface{}, 16),
        mRWMutex: new(sync.RWMutex),
    }
}

func (me *tBeanContainer) GetBean(name string) (bool, interface{}) {
    me.mRWMutex.RLock()
    defer me.mRWMutex.RUnlock()

    it, ok := me.mBeans[name]
    if ok {
        return true, it
    } else {
        return false, nil
    }
}

func (me *tBeanContainer) SetBean(name string, it interface{}) error {
    me.mRWMutex.Lock()
    defer me.mRWMutex.Unlock()

    if _,ok := me.mBeans[name];ok {
        return errors.New(fmt.Sprintf("bean with name %s already exists", name))
    }
    
    me.mBeans[name] = it
    return nil
}

type tContainedSingleton struct {}

func (me *tContainedSingleton) Hello() {
    fmt.Printf("tContainedSingleton.Hello\n")
}

func newContainedSingleton() IDemoSingleton {
    return &tContainedSingleton{}
}


var DefaultBeanContaibner = newBeanContainer()

func init() {
    DefaultBeanContaibner.SetBean("IDemoSingleton", newContainedSingleton())
}

单例模式小结

单例模式的优点
(1)单例模式可以保证内存里只有一个实例,减少了内存的开销。
(2)可以避免对资源的多重占用。
(3)单例模式设置全局访问点,可以优化和共享资源的访问。
单例模式的缺点
(1)单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
(2)在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
(3)单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。

(end)

你可能感兴趣的:(golang设计模式单例模式)