何时使用全局变量

use*_*761 14 go

我多次听说你应该避免全局变量.

在我的例子中,我声明了一个全局myTypes变量,只是为了避免在函数调用或类似的东西中反复声明该变量.

这是怎么做的?有没有更好的办法?一种更可测试的方式?

var myTypes = map[string]string{
  "type1": "tpl1",
  "type2": "tpl2",
}

func AFunc(someType string) string {
  fmt.Sprintf("this is your type %s", myTypes[someType])
}

func main() {
  AFunc("type1")
}
Run Code Online (Sandbox Code Playgroud)

Rol*_*lig 8

并非所有全局变量都是坏的.在你的情况下:

  • 全局变量在main包中,因此只能由单个程序访问.还行吧.
  • 全局变量初始化一次,之后不再修改.还行吧.

另一方面,每当在程序执行期间修改全局变量时,程序就变得更难理解.因此应该避免.

在一个可重用的包中,应该避免全局变量,因为那个包的两个用户可能会相互影响.想象一下,如果json包具有全局变量var Indent bool.这样的变量最好隐藏在数据结构中,就像JsonFormatter每次有人想要格式化一些JSON时重新创建一样.


Von*_*onC 8

一种常用方法是使用方法值

考虑struct type T使用两种方法,Mv其接收器是类型的T,并且Mp其接收器是类型的*T.

type T struct { 
    a int
}
func (tv  T) Mv(a int) int         { return 0 }  // value receiver
func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver

var t T
Run Code Online (Sandbox Code Playgroud)

表达方式

T.Mv
Run Code Online (Sandbox Code Playgroud)

产生一个等效于Mv的函数,但是有一个显式接收器作为它的第一个参数; 它有签名

func(tv T, a int) int
Run Code Online (Sandbox Code Playgroud)

您可以在此线程中看到方法值的示例

// TODO: Get rid of the global variable.
var foo service

func handleFoo(w http.ResponseWriter, req *http.Request) {
    // code that uses foo
}

func main() {
    foo = initFoo()

    http.HandleFunc("/foo", handleFoo)
}
Run Code Online (Sandbox Code Playgroud)

摆脱全局变量的一种方法是使用方法值:

type fooHandler struct {
    foo service
}

func (h fooHandler) handle(w http.ResponseWriter, req *http.Request) {
    // code that uses h.foo
}

func main() {
    foo := initFoo()

    http.HandleFunc("/foo", fooHandler{foo}.handle)
}
Run Code Online (Sandbox Code Playgroud)

Go 1.7中引入了一种新的官方方法来实现您的全局价值context.Context#Values.

仅将上下文值用于转换进程和API的请求范围数据,而不是将可选参数传递给函数.

请参阅" 如何在Go 1.7中正确使用context.Context "


最后,除了难以测试之外,全球价值观还可以防止出售.

请参阅" 供应商或不供应商,这是一个问题 "

许多Go的库都导出了包变量.这些变量可以被视为某个包的某些全局状态.

在以前的销售时代,我们可以获得每个导入的包一次,并且可以在所有其他导入的包中共享每个导入包的全局状态.

一些开发者可能会认为它是理所当然的,只是随意操纵这些全球状态.
但是,通过销售每个导入的包可能有自己的全局状态视图.现在,开发者可能会发现无法改变其他包的全局状态观