英文:
Sharing a global variable throughout packages
问题
所以我有一个需要在多个包中共享的 SQL 指针(*sql.DB
)。
例如:
"./main.go
" 有一个全局变量 "db
",需要与 "./another/package.go
" 中的一个包共享。
如何在不传递函数参数的情况下实现变量共享呢?
英文:
So I have a SQL pointer (*sql.DB
) that needs shared throughout packages.
For example:
"./main.go
" has a global variable "db
" that needs to be shared with a package in "./another/package.go
".
How could one achieve sharing variables without passing function parameters?
答案1
得分: 3
只要全局变量被导出(即其名称以大写字母开头:Db *sql.DB
),你就可以通过其完整名称在另一个包中访问它:
package.name.Db
但是,全局变量的替代方案是依赖注入,例如使用inject框架来初始化正确的数据库。
参见《Go语言中的依赖注入》:
inject
库是这项工作和我们的解决方案的结果。它使用struct
标签来启用注入,为具体类型分配内存,并支持对接口类型的注入,只要它们是明确的。它还具有一些不常用的功能,如命名注入。大致上,我们上面的简单示例现在看起来像这样:
type AppLoader struct {
MongoService mongo.Service `inject:""`
}
func (l *AppLoader) Get(id uint64) *App {
a := new(App)
l.MongoService.Session().Find(..).One(a)
return a
}
英文:
As long as the global variable is exported (meaning its name starts with an uppercase letter: Db *sql.DB
), you can access it in another package through its full name:
package.name.Db
But the alternative to global variable is dependency injection, as in using the inject framework to initialize the correct db.
See "Dependency Injection with Go":
> The inject
library is the result of this work and our solution.
It uses struct
tags to enable injection, allocates memory for concrete types, and supports injection for interface types as long as they’re unambiguous.
It also has some less often used features like named injection. Roughly, our naive example above now looks something like this:
type AppLoader struct {
MongoService mongo.Service `inject:""`
}
func (l *AppLoader) Get(id uint64) *App {
a := new(App)
l.MongoService.Session().Find(..).One(a)
return a
}
答案2
得分: 0
VonC的问题的替代方案是提供一个构造函数,例如:
// package datastore
var db *sql.DB
func NewDB(host, port string) (*sql.DB, error) {
// 简化示例
conn, err := sql.Open(...)
if err != nil {
return nil, err
}
db = conn
return conn, nil
}
// package main
func main() {
db, err := datastore.NewDB("localhost", "5432")
if err != nil {
log.Fatal(err)
}
// 现在你可以在这里使用它,或者在你的datastore包中使用它
}
通常最好使用构造函数来初始化一个包的要求,或者传入一个预先初始化的对象,例如datastore.NewFromExisting(db)
,以传入你已经创建的连接池。
在可能的情况下,你的package main
应该只是其他包的入口点,并尽量避免自己消耗资源。
英文:
The alternative to VonC's question is to provide a constructor - e.g.
// package datastore
var db *sql.DB
func NewDB(host, port string) (*sql.DB, error) {
// Simplified example
conn, err := sql.Open(...)
if err != nil {
return nil, err
}
db = conn
return conn, nil
}
// package main
func main() {
db, err := datastore.NewDB("localhost", "5432")
if err != nil {
log.Fatal(err)
}
// Now you can use it here, and/or in your datastore package
}
It's typically good practice to use constructors to initialize a package's requirements, and/or pass in a pre-initialized object - e.g. datastore.NewFromExisting(db)
to pass in a pool you've already created.
Where possible your package main
should simply be an entry point for other packages and should try to avoid consuming things on its own.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论