2013-02-15 2 views
4

В моей программе Go есть значения конфигурации, которые я хочу быть постоянными на время выполнения программы, но я хочу иметь возможность изменять их на месте развертывания. Насколько я могу судить, нет никакого способа добиться этого с помощью ключевого слова const, так как (опять же, насколько я могу судить) его значение должно быть константой, указанной в времени компиляции. Это означает, что единственный способ достичь того, что я хочу, - объявить нормальные переменные и инициализировать их во время функциипакета. Дело не в том, что это не сработает, а в том, что теперь не будет ничего, что бы не могло изменить эти значения псевдопостоянства.Константы в Go, установленные во время инициализации

Мои два вопроса:

  1. я упускаю что-то о том, как const работает?
  2. Предполагая, что я не являюсь, каким образом этот способ лучше всего подходит? Публичная функция, которая возвращает закрытую переменную, которую я никогда не раскрываю, никогда не меняя ее? Просто надеясь, что люди не изменят переменные, так как они действительно настройки конфигурации?
+2

Вы можете создать тип, содержащий личные переменные (устанавливаемые изнутри этого модуля, но не снаружи). Затем добавьте функции доступа к типу. Это не совсем то же самое, но это так. – Crisfole

+4

Еще лучше: создайте файл «config.go», создайте vars, который хотите открыть, но не экспортируйте их (все в нижнем регистре). Затем создайте общедоступный (верхний регистр) 'func', который дает доступ к каждому элементу. Не нужен тип, просто модуль. – Crisfole

+0

Если вы ответите, @ChristopherPfohl, я выберу его. –

ответ

2

Создайте файл «config.go» и создайте вары, которые вы хотите открыть.

Не экспортируйте их (сделайте их все в нижнем регистре). Вместо этого создайте общедоступный (верхний регистр) func, который дает доступ к каждому элементу.

package config 

var x = 10 

func X() int { 
    return x 
} 

Если вы хотите, чтобы эти переменные просто import ./config и использовать их в коде следующим образом:

if config.X() 

Очевидно, что вы можете установить переменные в пакете init.

1

Следующий код почти такой же, как второй метод @Christopher, за исключением того, что он не является модулем, он находится в основном пакете.

package main 

import (
    "os" 
) 

type Config struct { 
    debug    bool 
    key     string 
    proxyNumber   int 
} 

func (c *Config) Debug() bool { 
    return c.debug 
} 
func (c *Config) Key() string { 
    return c.key 
} 
func (c *Config) ProxyNumber() int { 
    return c.proxyNumber 
} 

const (
    CONFIG_NAME = "config.ini" 
) 

var config *Config 

func init() { 
    DefaultConfig() 
    if Exists(CONFIG_NAME) { 
     //try to save the config file 
    }else { 
     //try to load from the config file 

    } 
} 

func DefaultConfig() { 
    config = &Config{debug:true, key:"abcde", 
     proxyNumber:5, 
    } 
} 

//Exist: check the file exist 
func Exists(path string) bool { 
    _, err := os.Stat(path) 
    if err == nil { return true } 
    if os.IsNotExist(err) { return false } 
    return false 
} 

вы можете использовать config, чтобы загрузить и сохранить файл конфигурации.

+0

Хороший звонок, я видел эту идиому где-то еще недавно, не могу вспомнить, где – Crisfole

1

Это очень хороший вопрос, потому что он вникает в то, что, как я подозреваю, может быть упущением из Го - неизменяемого состояния.

Из language reference «константные выражения могут содержать только константные операнды и оцениваться во время компиляции».

Вы не можете сделать варс постоянным, что является позором. Ответ Джо предлагает инкапсуляцию как решение, которое будет хорошо работать, но оно многословно, утомительно и может привести к ошибкам.

Для сравнения, многие нечистые функциональные языки объединяют изменяемые переменные с неизменяемыми значениями с одним присваиванием. Например, у Scala есть ключевые слова «val» и «var»; значение «var» Scala весьма похоже на «var». Неизменность - полезный инструмент в панели инструментов, потому что могут быть написаны ссылочно-прозрачные функции, свободные от побочных эффектов, наряду с программным кодом с изменением состояния. Оба имеют свое место. Неизменность также является ценным инструментом для параллелизма, потому что нет беспокойства о возможных условиях гонки, если неизменные ценности распределяются между гортанами.

Так что, на мой взгляд, среди многих его сильных сторон это один из недостатков Го. По-видимому, было бы нелегко поддерживать vals, а также vars, причем разница заключалась в том, что компилятор проверяет, что каждый val назначается ровно один раз.

Пока эта функция не будет добавлена, у вас есть инкапсуляция как единственный вариант.

+0

PS обратите внимание на то, что строки Go являются внутренне неизменными. Тем не менее, var, который содержит строку, сам по себе изменчив, даже если строка, которую он держит, является неизменной. –

+0

PS Здесь обсуждается это: https://groups.google.com/forum/?fromgroups=#!searchin/golang-nuts/immutable/golang-nuts/BnjG3N77Ico/5_Unx7u2tusJ –

+0

Поскольку у Go есть указатели, не будет проверка единственного назначения невозможна? – Crisfole

0

Вы можете сделать что-то вроде этого:

package main 

import (
    "fmt" 
    "strconv" 
) 

var a string 

func main() { 
    myvar, err := strconv.Atoi(a) 
    if err != nil { 
     fmt.Println(err) 
    } 
    fmt.Println(myvar) 
} 

и скомпилировать программу с

go build -ldflags '-X main.a 10' test.go 

Таким образом, вы можете определить константу во время компиляции.

+1

Это интересная идея. Однако нет ничего, что мешало бы «а» быть измененным, кроме его ограниченной сферы. Таким образом, это не постоянный, а больше параметр конфигурации. –

+0

Хотя, поскольку прямой Go не имеет параметров времени компиляции, это все еще полезно в других контекстах. –

0

Просто используйте стандарт go flags с iniflags. Стандартные флаги go позволяют устанавливать произвольные конфигурационные переменные при запуске программы, передавая флаги командной строки, а iniflags «magically» добавляют поддержку для чтения конфигурационных переменных из ini-файлов.

Смежные вопросы