I've heard many times that you should avoid global variables.
In my example I declared a global myTypes
variable only to avoid declaring that variable over and over again in a function call or something similar.
Is this how it should be done? Is there a better way? A more testable way?
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")
}
One usual way is to use Method Value
Consider a
struct type T
with two methods,Mv
, whose receiver is of typeT
, andMp
, whose receiver is of type*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
The expression
T.Mv
yields a function equivalent to Mv but with an explicit receiver as its first argument; it has signature
func(tv T, a int) int
You can see an example of Method Value 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)
}
One way to get rid of that global variable is to use method values:
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)
}
A new official approach for your global values is introduced in Go 1.7 with context.Context#Values
.
Use context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions.
See "How to correctly use context.Context in Go 1.7"
Finally, in addition of being hard to test, global values can prevent vendoring.
See "To vendor or not to vendor, that is a question"
Many Go’s libaries have exported package variables. Those variables can be viewed as certain global states of a certain package.
Prior vendoring era, we can go get each imported package once and the global state of each imported package can be shared within all other imported packages.
Some devs may take it as granted and simply manipulate those global states at will.
However, with vendoring each imported package may have its own view of global states. Now a dev may found it impossible to change other package’s view of global state
Not all global variables are bad. In your case:
main
package and therefore only accessible by a single program. This is ok.On the other hand, whenever a global variable is modified during the program’s execution, the program becomes more difficult to understand. Therefore it should be avoided.
In a package that is meant to be reusable, global variables should be avoided since then two users of that package might influence each other. Imagine if the json
package had a global variable var Indent bool
. Such a variable is better to be hidden inside a data structure like JsonFormatter
that gets created anew each time someone wants to format some JSON.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With