Когда использовать глобальные переменные

Я много раз слышал, что вы должны избегать глобальных переменных.

в моем примере я объявил глобальный 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")
}

2 ответов


один обычный способ-использовать Способ Значение

рассмотрим 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

выражение

T.Mv

дает функцию, эквивалентную Mv, но с явным приемником в качестве первого аргумента; он имеет подпись

func(tv T, a int) int

вы можно увидеть пример метода Value в этой теме

// 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, а не для передачи дополнительных параметров функциям.

в разделе "Как правильно использовать контекст.Контекст в Go 1.7"


наконец, в дополнение к тому, что трудно проверить, глобальные значения могут предотвратить vendoring.

в разделе "поставщику или не поставщику, это вопрос"

многие библиотеки Go экспортировали переменные пакета. Эти переменные можно рассматривать как определенные глобальные состояния определенного пакета.

предыдущая вендоринг эра, мы можем пойти получить каждый импортированный пакет раз и глобальное положение каждого импортированного пакета можно делить внутри все другие импортированные пакеты.

некоторые разработчики могут принять это как должное и просто манипулировать этими глобальными государствами по своему желанию.
Однако,С vendoring каждый импортированный пакет может иметь свой собственный взгляд глобальных государств. Теперь dev может оказаться невозможным изменить представление другого пакета о глобальном состоянии


не все глобальные переменные-это плохо. В вашем случае:

  • глобальная переменная находится в main пакет и, следовательно, доступен только одной программой. Это нормально.
  • глобальная переменная инициализируется один раз и не изменяется в дальнейшем. Это нормально.

С другой стороны, если глобальная переменная изменяется во время выполнения программы, программа становится более сложной для понимания. Поэтому он должен быть избежавший.

в пакете, который предназначен для повторного использования, следует избегать глобальных переменных, поскольку тогда два пользователя этого пакета могут влиять друг на друга. Представьте себе, если json пакет имел глобальную переменную var Indent bool. Такую переменную лучше спрятать внутри структуры данных типа JsonFormatter это создается заново каждый раз, когда кто-то хочет отформатировать JSON.