На наступне питання є кілька відповідей / методів:
- Як встановити значення за замовчуванням для голанг-структур?
- Як ініціалізувати структури в голангу
У мене є пара відповідей, але подальше обговорення потрібно.
На наступне питання є кілька відповідей / методів:
У мене є пара відповідей, але подальше обговорення потрібно.
Відповіді:
Одна можлива ідея - написати окрему функцію конструктора
//Something is the structure we work with
type Something struct {
Text string
DefaultText string
}
// NewSomething create new instance of Something
func NewSomething(text string) Something {
something := Something{}
something.Text = text
something.DefaultText = "default text"
return something
}
NewSomething
та навіть поля Text
та DefaultText
, але просто не експортувати тип структури something
.
reflect.New()
наприклад, наприклад), не можна було б очікувати, що ви знаєте про вашу спеціально названу заводську функцію. У такому випадку, якщо не змінювати мову, міг би вважатись лише інтерфейс (який би могла перевірити бібліотека).
Примусовий метод отримати структуру (конструкторський шлях).
Хороший дизайн полягає в тому, щоб зробити ваш тип не експортованим, але забезпечити функцію експортованого конструктора, як NewMyType (), в якій можна правильно ініціалізувати структуру / тип. Також поверніть тип інтерфейсу, а не конкретний тип, і інтерфейс повинен містити все, що інші хочуть зробити з вашим значенням. І ваш конкретний тип повинен звичайно реалізувати цей інтерфейс.
Це можна зробити, просто зробивши сам тип не експортованим. Ви можете експортувати функцію NewSomething і навіть поля Text та DefaultText, але просто не експортуйте структуру
Інший спосіб налаштувати його під власний модуль - це використовувати структуру Config для встановлення значень за замовчуванням (Варіант 5 за посиланням).
Однією з проблем Віктора Заманяна з варіантом 1 є те, що якщо тип не експортується, користувачі вашого пакету не можуть оголосити його як тип для параметрів функції тощо. Одним із способів цього було б експортувати інтерфейс замість структура, наприклад
package candidate
// Exporting interface instead of struct
type Candidate interface {}
// Struct is not exported
type candidate struct {
Name string
Votes uint32 // Defaults to 0
}
// We are forced to call the constructor to get an instance of candidate
func New(name string) Candidate {
return candidate{name, 0} // enforce the default value here
}
Що дозволяє нам оголошувати типи параметрів функції за допомогою експортованого інтерфейсу Candidate. Єдиний недолік, який я бачу в цьому рішенні, - це те, що всі наші методи потрібно задекларувати у визначенні інтерфейсу, але ви можете стверджувати, що це все-таки є хорошою практикою.
Votes unit32
мабуть, має бутиVotes uint32
Існує спосіб зробити це за допомогою тегів, який дозволяє зробити кілька за замовчуванням.
Припустимо, у вас є така структура з двома тегами за замовчуванням 0 та default1 .
type A struct {
I int `default0:"3" default1:"42"`
S string `default0:"Some String..." default1:"Some Other String..."`
}
Тепер можна встановити параметри за замовчуванням.
func main() {
ptr := &A{}
Set(ptr, "default0")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=3 ptr.S=Some String...
Set(ptr, "default1")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=42 ptr.S=Some Other String...
}
Ось повна програма на дитячому майданчику .
Якщо вас цікавить складніший приклад, скажімо, з фрагментами та картами, тоді погляньте на кресті / defaultse
З https://golang.org/doc/effective_go.html#composite_literals :
Іноді нульове значення недостатньо добре, і потрібен конструктор ініціалізації, як у цьому прикладі, отриманому з пакета os.
func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
f := new(File)
f.fd = fd
f.name = name
f.dirinfo = nil
f.nepipe = 0
return f
}
type Config struct {
AWSRegion string `default:"us-west-2"`
}