手撸 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)