如何解决Golang中的golang-ci lint问题
如何解决Golang中的golang-ci lint问题
我编写了以下代码来仅创建一次 dbSession,但我从 golang ci-lint 收到了以下警告:
once 是一个全局变量 (gochecknoglobals)
导出的 func NewDbSession 返回了未导出的类型 dbSession,这在使用时可能会令人困扰 (golint)
var (
once sync.Once
dbSess dbSession
)
type dbSession struct {
session *gocql.Session
}
func NewDbSession() dbSession {
once.Do(func() {
if dbSess.session == nil {
session, err := createSession()
if err != nil {
panic(err)
}
dbSess = dbSession{session: session}
}
})
return dbSess
}
func createSession() {
....
}
这被认为是 Go 语言中实现单例模式的惯用方式。
- 首先,我们如何在不使用全局变量的情况下实现单例?
- 其次,有时我们不想通过导出的函数暴露类型。在 Go 语言中,对于返回值的导出函数,是否必须拥有导出的返回类型,这是一条硬性规定吗?
更多关于如何解决Golang中的golang-ci lint问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
恰当的做法是公开类型,以便人们可以使用它来论证和推理自己的代码,同时不暴露其内部字段。
更多关于如何解决Golang中的golang-ci lint问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
lutzhorn:
你可以将全局变量命名为
_once,这样是可以的。(参见 https://github.com/leighmcculloch/gochecknoglobals#exceptions)
这并没有解决问题。
我仍然收到 _once 是一个全局变量 (gochecknoglobals) 的错误。
siddhanta_rath:
- 首先,我们如何在不使用全局变量的情况下实现单例模式?
你可以将全局变量命名为 _once,这是允许的。(参见 GitHub - leighmcculloch/gochecknoglobals: Check that no globals are present in Go code.)
siddhanta_rath:
- 其次,有时我们不想通过导出的函数暴露类型。
为什么?
为什么?
- 避免运行时错误
- 实现封装
为什么 golint 将其视为警告?
在 Go 中,使用这种模式/结构很常见。
type sample struct {
// 字段放在这里
}
fun NewSample(/* 输入变量 */) *sample {
// 特定的初始化逻辑放在这里
// 此包不直接导出 sample 结构体
return &s
}
golint 会警告:“exported func NewSample returns unexported type *main.sample, which can be annoying to use (golint)”
关于golangci-lint警告的专业解答
1. 解决全局变量警告 (gochecknoglobals)
您可以使用包级变量但避免全局状态的方式重构代码。以下是几种解决方案:
方案A:使用sync.Once作为结构体字段
type Database struct {
once sync.Once
session *gocql.Session
}
var dbInstance *Database
func GetDatabase() *Database {
if dbInstance == nil {
dbInstance = &Database{}
}
dbInstance.once.Do(func() {
var err error
dbInstance.session, err = createSession()
if err != nil {
panic(err)
}
})
return dbInstance
}
方案B:完全避免包级变量(推荐)
type database struct {
session *gocql.Session
}
var (
dbInstance *database
dbOnce sync.Once
)
func GetSession() *gocql.Session {
dbOnce.Do(func() {
session, err := createSession()
if err != nil {
panic(err)
}
dbInstance = &database{session: session}
})
return dbInstance.session
}
2. 解决导出函数返回未导出类型的问题 (golint)
方案A:返回接口而不是具体类型
type Session interface {
Query(stmt string, values ...interface{}) *gocql.Query
Close()
// 其他需要的方法
}
type dbSession struct {
session *gocql.Session
}
func (d *dbSession) Query(stmt string, values ...interface{}) *gocql.Query {
return d.session.Query(stmt, values...)
}
func (d *dbSession) Close() {
d.session.Close()
}
func NewDbSession() Session {
var instance *dbSession
once.Do(func() {
session, err := createSession()
if err != nil {
panic(err)
}
instance = &dbSession{session: session}
})
return instance
}
方案B:返回导出的结构体包装器
type DBSession struct {
session *gocql.Session
}
func (d *DBSession) Query(stmt string, values ...interface{}) *gocql.Query {
return d.session.Query(stmt, values...)
}
func NewDbSession() *DBSession {
var instance *DBSession
once.Do(func() {
session, err := createSession()
if err != nil {
panic(err)
}
instance = &DBSession{session: session}
})
return instance
}
方案C:返回函数闭包(当只需要少数方法时)
type SessionFunc func(stmt string, values ...interface{}) *gocql.Query
func NewDbSession() SessionFunc {
var session *gocql.Session
once.Do(func() {
var err error
session, err = createSession()
if err != nil {
panic(err)
}
})
return func(stmt string, values ...interface{}) *gocql.Query {
return session.Query(stmt, values...)
}
}
// 使用示例
queryFunc := NewDbSession()
query := queryFunc("SELECT * FROM users")
3. 完整的最佳实践示例
package database
import (
"sync"
"github.com/gocql/gocql"
)
type Session interface {
Query(stmt string, values ...interface{}) *gocql.Query
Close() error
ExecuteBatch(batch *gocql.Batch) error
}
type cassandraSession struct {
once sync.Once
session *gocql.Session
}
func (cs *cassandraSession) initialize() error {
var initErr error
cs.once.Do(func() {
cluster := gocql.NewCluster("127.0.0.1")
cluster.Keyspace = "mykeyspace"
session, err := cluster.CreateSession()
if err != nil {
initErr = err
return
}
cs.session = session
})
return initErr
}
func (cs *cassandraSession) Query(stmt string, values ...interface{}) *gocql.Query {
if err := cs.initialize(); err != nil {
panic(err)
}
return cs.session.Query(stmt, values...)
}
func (cs *cassandraSession) Close() error {
if cs.session != nil {
return cs.session.Close()
}
return nil
}
func (cs *cassandraSession) ExecuteBatch(batch *gocql.Batch) error {
if err := cs.initialize(); err != nil {
return err
}
return cs.session.ExecuteBatch(batch)
}
var globalSession *cassandraSession
var globalOnce sync.Once
func GetSession() Session {
globalOnce.Do(func() {
globalSession = &cassandraSession{}
})
return globalSession
}
关于Go语言规范的说明:
-
导出函数返回未导出类型:这不是语法错误,但违反了Go的导出约定。导出的函数应该返回导出的类型,否则调用者无法声明该类型的变量或将其作为参数传递。
-
单例模式实现:在Go中,通常使用包级函数配合
sync.Once来实现单例,但可以通过将状态封装在结构体中来减少全局变量的使用。
这些修改既符合golangci-lint的规范,也遵循了Go语言的惯用写法。

