手撸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)
有疑问加站长微信联系(非本文作者)