英文:
Global Vars in Golang
问题
我一直在阅读一本关于Golang的书,它常常使用全局变量来公开私有函数的结果。为什么不直接公开公共函数呢?这里有一个例子。
我来自Java,这对我来说有点棘手。使用这样的全局变量有什么好处呢?
编辑:我注意到,没有括号,我并没有调用函数。它只是一个指针。这是一种好的做法还是有一些优势呢?
package config
import "time"
var (
GetHTTPServerAddress = getHTTPServerAddress
GetHTTPReadTimeout = getHTTPReadTimeout
GetHTTPWriteTimeout = getHTTPWriteTimeout
)
func getHTTPServerAddress() string {
return getConfigString("http.server_address")
}
func getHTTPReadTimeout() time.Duration {
return getConfigDuration("http.read_timeout")
}
func getHTTPWriteTimeout() time.Duration {
return getConfigDuration("http.write_timeout")
}
英文:
I've been reading a book about Golang and it's common use global vars to expose the result of private functions. Why not to expose public functions? here's an exemple.
I am comming from java and this is tricky for me. What are advantages of using global vars like these?
EDIT: I notice that without parenthesis I am not calling the function. It's just a pointer. Is it a good pratice or does have some advantages?
package config
import "time"
var (
GetHTTPServerAddress = getHTTPServerAddress
GetHTTPReadTimeout = getHTTPReadTimeout
GetHTTPWriteTimeout = getHTTPWriteTimeout
)
func getHTTPServerAddress() string {
return getConfigString("http.server_address")
}
func getHTTPReadTimeout() time.Duration {
return getConfigDuration("http.read_timeout")
}
func getHTTPWriteTimeout() time.Duration {
return getConfigDuration("http.write_timeout")
}
答案1
得分: 5
我一直在阅读一本关于Golang的书,它常常使用全局变量来暴露私有函数的结果。
为了暴露私有函数的结果,而不是函数本身;你的例子有点不对。没有括号,你暴露的是函数本身。
package main
import (
"fmt"
)
var(
FooItself = foo // (func() string)(0x108b720)
FooResult = foo() // "bar"
)
func main() {
fmt.Printf("%#v\n", FooItself)
fmt.Printf("%#v\n", FooResult)
}
func foo() string {
return "bar"
}
试一试。
你的例子应该像这样。
package config
var (
// 你不会将变量命名为"Get*",那样会暴露实现细节。
HTTPServerAddress = getHTTPServerAddress()
HTTPReadTimeout = getHTTPReadTimeout()
HTTPWriteTimeout = getHTTPWriteTimeout()
)
func getHTTPServerAddress() string {
return getConfigString("http.server_address")
}
func getHTTPReadTimeout() time.Duration {
return getConfigDuration("http.read_timeout")
}
func getHTTPWriteTimeout() time.Duration {
return getConfigDuration("http.write_timeout")
}
但是并不需要私有方法。只需运行函数并将其赋值给全局变量。
package config
var (
HTTPServerAddress = getConfigString("http.server_address")
HTTPReadTimeout = getConfigDuration("http.read_timeout")
HTTPWriteTimeout = getConfigDuration("http.write_timeout")
)
现在人们可以使用config.HTTPServerAddress
,而细节仍然隐藏,无需额外的方法层。
是的,可以在公共函数的结果上这样做。一个非常常见的例子是错误代码。例如,在net/http
中。
var (
ErrBodyNotAllowed = errors.New("http: request method or response status code does not allow body")
ErrHijacked = errors.New("http: connection has been hijacked")
)
在Java中,你可能会为每种类型的错误创建一个异常子类。在Go中,你只需创建一个对象,将其作为全局变量共享,然后所有引用该对象的代码都会生效。
目的是隐藏细节。错误的确切措辞可能会改变,但只要每个人都引用net.http.ErrBodyNotAllowed
,他们的代码仍然可以工作。
io/fs
有一个更复杂的例子,它暴露了私有方法的结果。
var (
ErrInvalid = errInvalid() // "invalid argument"
ErrPermission = errPermission() // "permission denied"
ErrExist = errExist() // "file already exists"
ErrNotExist = errNotExist() // "file does not exist"
ErrClosed = errClosed() // "file already closed"
)
func errInvalid() error { return oserror.ErrInvalid }
func errPermission() error { return oserror.ErrPermission }
func errExist() error { return oserror.ErrExist }
func errNotExist() error { return oserror.ErrNotExist }
func errClosed() error { return oserror.ErrClosed }
为什么在这里使用私有方法包装?为什么不直接使用ErrInvalid = oserror.ErrInvalid
?
(我猜)Go希望将oserror
隐藏起来,甚至连文档中也不显示。
oserror
只是创建错误对象来定义其可能的错误。
package oserror
import "errors"
var (
ErrInvalid = errors.New("invalid argument")
ErrPermission = errors.New("permission denied")
ErrExist = errors.New("file already exists")
ErrNotExist = errors.New("file does not exist")
ErrClosed = errors.New("file already closed")
)
英文:
> I've been reading a book about Golang and it's common use global vars to expose the result of private functions.
To expose the result of private functions, not the functions themselves; your example is a little off. Without the parens you're exposing the function itself.
package main
import (
"fmt"
)
var(
FooItself = foo // (func() string)(0x108b720)
FooResult = foo() // "bar"
)
func main() {
fmt.Printf("%#v\n", FooItself)
fmt.Printf("%#v\n", FooResult)
}
func foo() string {
return "bar"
}
Your example would look more like this.
package config
var (
// You wouldn't call the variable "Get*", that exposes
// the implementation.
HTTPServerAddress = getHTTPServerAddress()
HTTPReadTimeout = getHTTPReadTimeout()
HTTPWriteTimeout = getHTTPWriteTimeout()
)
func getHTTPServerAddress() string {
return getConfigString("http.server_address")
}
func getHTTPReadTimeout() time.Duration {
return getConfigDuration("http.read_timeout")
}
func getHTTPWriteTimeout() time.Duration {
return getConfigDuration("http.write_timeout")
}
But no private methods are necessary. Just run the function and assign it to a global.
package config
var (
HTTPServerAddress = getConfigString("http.server_address")
HTTPReadTimeout = getConfigDuration("http.read_timeout")
HTTPWriteTimeout = getConfigDuration("http.write_timeout")
)
Now people can use config.HTTPServerAddress
and the details remain hidden without requiring an extra layer of methods.
And yes, it can be done on the result public functions. A very common example is error codes. For example, in net/http
.
var (
ErrBodyNotAllowed = errors.New("http: request method or response status code does not allow body")
ErrHijacked = errors.New("http: connection has been hijacked")
)
In Java you might create an exception subclass for each type of error. In Go you make a single object, share it as a global variable, and everything references that object.
The purpose is to hide the details from the user. The exact wording of the error can change, but as long as everyone is referencing net.http.ErrBodyNotAllowed
their code will still work.
io/fs
has a more complex example exposing the result of its private methods.
var (
ErrInvalid = errInvalid() // "invalid argument"
ErrPermission = errPermission() // "permission denied"
ErrExist = errExist() // "file already exists"
ErrNotExist = errNotExist() // "file does not exist"
ErrClosed = errClosed() // "file already closed"
)
func errInvalid() error { return oserror.ErrInvalid }
func errPermission() error { return oserror.ErrPermission }
func errExist() error { return oserror.ErrExist }
func errNotExist() error { return oserror.ErrNotExist }
func errClosed() error { return oserror.ErrClosed }
Why use private method wrappers here? Why not ErrInvalid = oserror.ErrInvalid
?
(I'm assuming) Go wants to keep oserror
hidden, even from the documentation!
oserror
is just making error objects to define its possible errors.
package oserror
import "errors"
var (
ErrInvalid = errors.New("invalid argument")
ErrPermission = errors.New("permission denied")
ErrExist = errors.New("file already exists")
ErrNotExist = errors.New("file does not exist")
ErrClosed = errors.New("file already closed")
)
答案2
得分: 2
如果这是一个函数的结果,那么它可能是类似于static
成员初始化或者甚至是static final
的东西。
你提供的示例表明这可能是某种配置加载过程,并且暴露了全局使用的值。
预计算数据也是常见的做法。
然而,如果你看到函数指针,可能是为了实现模拟。
具体情况可能有所不同。有时候这也只是一种反模式。
英文:
If it's result of a function, it's probably something like static
member initialization or perhaps even static final
.
The example you linked suggests that it's some sort of config load procedure and values are exposed for global use.
Precomputing data is also not unheard of.
If you see function pointers, however, it could be done to enable mocking.
YMMV. Sometimes it's just an antipattern as well.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论