何时使用全局变量

时间:2016-07-24 12:36:12

标签: 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")
}

3 个答案:

答案 0 :(得分:7)

并非所有全局变量都不好。在你的情况下:

  • 全局变量位于main包中,因此只能由单个程序访问。没关系。
  • 全局变量初始化一次,之后不再修改。没关系。

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

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

答案 1 :(得分:6)

一种常用方法是使用Method Value

  

考虑struct type T使用两种方法Mv,其接收方类型为TMp,其接收方类型为*T。< / p>

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
  

表达式

T.Mv
  

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

func(tv T, a int) int

您可以看到方法值in this thread

的示例
// 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)
}
  

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

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)
}

Go 1.7中引入了context.Context#Values的全新价值观的新官方方法。

  

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

请参阅“How to correctly use context.Context in Go 1.7

最后,除了难以测试之外,全球价值可以阻止销售

请参阅“To vendor or not to vendor, that is a question

  

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

     

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

     

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

答案 2 :(得分:0)

您无需修改​​myTypes,而只是阅读它,因此它根本不是变量,它是一个常数,并且如果Go支持它,则将其定义为此类(并确保您不对其进行突变) -不幸的是,Go不允许您像其他语言一样强制执行“ constness”)。全局常数通常都很好。

如果您要修改myTypes,例如通过提供一个在运行时添加新类型的功能,那么是的,将myTypes保留为全局状态是一个坏主意。只要您仅在“主程序”中执行此操作,您就可能会摆脱它,确定不会将其作为其他代码导入的包,但是您不知道该代码可能在哪里结束/被复制到(甚至只是从同一包中的多个位置使用),所以为什么要冒险。如果这成为一个由其他软件包导入的软件包,则只​​要运行时活动的“客户端”软件包不超过一个,就可以正常工作,但是只要有人将多个此类软件包链接到一个二进制文件中,所有这些用户程序包将使全局myTypes中彼此的数据脚。如果一个客户程序包只希望看到它早些时候放入的myTypes,那么如果另一个客户的期望值不同,这将中断。因此,单独使用时可以正常工作的程序包一起使用时可能会损坏,除非更改共享代码,否则无法解决此问题。因此,请不要这样做。令人遗憾的是,Google自己在自己的某些公共物品中使用了全球状态,例如在标准的“标志”包中,使用诸如“ glog”之类的东西,从而继承了问题。不要这样做。