golang基础:项目实战单例模式,操作地址遇到的坑 | 青训营笔记

138 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第10篇笔记; 今天我尝试进行一些开发,以下就是开发新增的知识

单例模式

我们都知道java中的spring有ioc容器,容器中有很多工具,默认情况下,多次使用的容器中已有的某个工具类,用的都是同一个工具实例,这就是单例模式,同样golang中也有,不过和spring中使用不太一样,以下简略介绍一下:

单个go文件实现单例模式

...
type PostDao struct {//首先声明一个结构体,用于下面构建结构体内方法
}

var (
   postDao  PostDao
   postOnce sync.Once
)//声明两个变量
//sync.Once是Go语言实现的一种对象,用来保证某种行为只会被执行一次。sync.Once是由Once结构体和其Do,doSlow俩个方法实现的
func NewPostDaoInstance() PostDao {
     //其中Once.Do(f func())是一个挺有趣的东西,能保证once只执行一次,无论你是否更换once.Do(xx)这里的方法,这个sync.Once块只会执行一次。
   postOnce.Do(func() {
      postDao = PostDao{}
   })
   return postDao
}

// AddPost 添加评论入库
// post 需要添加的实体
func (PostDao) AddPost(post *po.Post) error {
   return db.Create(*post).Error
}
...

使用时直接这样调用: xxx.NewPostDaoInstance().AddPost(...)

接口实现类实现接口构建单例模式

接口:

type User interface {
   Insert(user *po.User) error
   }

接口实现结构体:

type UserImpl struct {
}
var (
   user     repositories.User//这个repositories.User就是上面声明的接口
   userOnce sync.Once
)
//单例模式,下次使用如果有这个实例就用这个实例,没有时再创建
func NewUserInstance() repositories.User {
   userOnce.Do(func() {
      user = UserImpl{}
   })
   return user
}
func (UserImpl) Insert(user *po.User) error {
....
}

使用时直接这样调用:xxx.NewUserInstance().Insert(...)

地址使用遇到的坑

遇到一种情况,如下:

....
//(src *[]某种自定义类型)        
//即src是某种自定义类型的切片
var cu map[int]*某种自定义类型 = make(map[int]*某种自定义类型, xxx)
for i, sr := range *src {
    cu[i]=&sr
...

这样写是错误的,因为map映射里存储的是地址,直接这样赋值的话每次循环后都会更新地址,而所有的map映射的值都指向同一个地址,所以map映射里存储的都是同一个值,就是循环最后一个值的地址,都是最后那一个值

那这怎么办,我们都知道当数据量超大的时候存储地址能极大的节省空间还有时间,我们可以copy一个副本,然后赋值这个副本,如下:

....
//(src *[]某种自定义类型)        
//即src是某种自定义类型的切片
var cu map[int]*某种自定义类型 = make(map[int]*某种自定义类型, xxx)
for i, sr := range *src {
    //用地址可以更省空间,但是也容易出错
    temp := &sr     //temp是sr地址
    temp1 := *temp  //temp1是temp的实体,也就是sr的数据实体
    temp2 := &temp1 //temp2是temp1的地址,
    cu[i]=temp2
...

这样就完美解决了这个问题;