Конструкторы в Go

У меня есть структура, и я бы хотел, чтобы она была инициализирована с некоторыми разумными значениями по умолчанию.

Как правило, здесь нужно использовать конструктор, но поскольку go не является ООП в традиционном смысле, это не истинные объекты, и он не имеет конструкторов.

Я заметил метод init, но на уровне пакета. Есть ли что-то подобное, что можно использовать на уровне структуры?

Если не то, что является принятой лучшей практикой для этого типа вещей в Go?

Ответ 1

На самом деле существуют две приемлемые рекомендации:

  • Сделать нулевое значение вашей структуры разумным дефолтом. (Хотя это выглядит странно для большинства людей, поступающих из "традиционного", это часто работает и действительно удобно).
  • Предоставить функцию func New() YourTyp или если у вас есть несколько таких типов в ваших функциях пакета func NewYourType1() YourType1 и т.д.

Документ, если нулевое значение вашего типа доступно или нет (в этом случае оно должно быть настроено одной из функций New.... (Для "традиционалистов" oops: тот, кто не читает документацию, выиграл не сможете правильно использовать ваши типы, даже если он не может создавать объекты в состояниях undefined.)

Ответ 2

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

Предположим, что у вас есть такая структура:

type Thing struct {
    Name  string
    Num   int
}

тогда, если нулевые значения не подходят, вы обычно строите экземпляр с функцией NewThing, возвращающей указатель:

func NewThing(someParameter string) *Thing {
    p := new(Thing)
    p.Name = someParameter
    p.Num = 33 // <- a very sensible default value
    return p
}

Когда ваша структура достаточно проста, вы можете использовать эту сконденсированную конструкцию:

func NewThing(someParameter string) *Thing {
    return &Thing{someParameter, 33}
}

Если вы не хотите возвращать указатель, то практика заключается в вызове функции makeThing вместо NewThing:

func makeThing(name string) Thing {
    return Thing{name, 33}
}

Ссылка: Распределение с новым в Эффективном Go.

Ответ 3

Go имеет объекты. Объекты могут иметь конструкторы (хотя и не автоматические конструкторы). И, наконец, Go - это язык ООП (типы данных имеют приложенные методы, но, по общему признанию, существуют бесконечные определения того, что такое ООП.)

Тем не менее, принятая наилучшая практика состоит в том, чтобы написать ноль или более конструкторов для ваших типов.

Как @dystroy отправил свой ответ, прежде чем я закончил этот ответ, позвольте мне просто добавить альтернативную версию его конструктора примеров, которую я бы, вероятно, написал вместо:

func NewThing(someParameter string) *Thing {
    return &Thing{someParameter, 33} // <- 33: a very sensible default value
}

Причина, по которой я хочу показать вам эту версию, заключается в том, что довольно часто "встроенные" литералы могут использоваться вместо вызова "конструктора".

a := NewThing("foo")
b := &Thing{"foo", 33}

Теперь *a == *b.

Ответ 4

В Go нет конструкторов по умолчанию, но вы можете объявлять методы для любого типа. Вы могли бы привыкнуть объявлять метод "Init". Не уверен, как это относится к передовым методам, но помогает сохранить короткие имена, не теряя ясности.

package main

import "fmt"

type Thing struct {
    Name string
    Num int
}

func (t *Thing) Init(name string, num int) {
    t.Name = name
    t.Num = num
}

func main() {
    t := new(Thing)
    t.Init("Hello", 5)
    fmt.Printf("%s: %d\n", t.Name, t.Num)
}

Результат:

Hello: 5

Ответ 5

Мне нравится объяснение из этого сообщения в блоге:

Функция New - это соглашение Go для пакетов, которые создают основной тип или различные типы для использования разработчиком приложения. Посмотрите, как New определяется и реализуется в log.go, bufio.go и cypto.go:

log.go

// New creates a new Logger. The out variable sets the
// destination to which log data will be written.
// The prefix appears at the beginning of each generated log line.
// The flag argument defines the logging properties.
func New(out io.Writer, prefix string, flag int) * Logger {
    return &Logger{out: out, prefix: prefix, flag: flag}
}

bufio.go

// NewReader returns a new Reader whose buffer has the default size.
func NewReader(rd io.Reader) * Reader {
    return NewReaderSize(rd, defaultBufSize)
}

crypto.go

// New returns a new hash.Hash calculating the given hash function. New panics
// if the hash function is not linked into the binary.
func (h Hash) New() hash.Hash {
    if h > 0 && h < maxHash {
        f := hashes[h]
        if f != nil {
            return f()
        }
    }
    panic("crypto: requested hash function is unavailable")
}

Поскольку каждый пакет действует как пространство имен, каждый пакет может иметь свою собственную версию New. В bufio.go может быть создано несколько типов, поэтому нет отдельной функции New. Здесь вы найдете такие функции, как NewReader и NewWriter.

Ответ 6

другой способ:

package person

type Person struct {
    Name string
    Old  int
}

func New(name string, old int) *Person {
    // set only specific field value with field key
    return &Person{
        Name: name,
    }
}

Ответ 7

Если вы хотите принудительно использовать фабричную функцию, назовите вашу структуру (ваш класс) первым символом в нижнем регистре. Тогда невозможно будет непосредственно создать экземпляр структуры, потребуется фабричный метод.

Эта видимость на основе первого символа в нижнем/верхнем регистре работает также для структурного поля и для функции/метода. Если вы не хотите разрешать внешний доступ, используйте строчные буквы.

Ответ 8

Голанг не является языком ООП в его официальных документах. Все поля структуры Golang имеют определенное значение (не похоже на c/С++), поэтому функция-конструктор не так необходима, как cpp. Если вам нужно присвоить некоторые поля некоторым специальным значениям, используйте функции factory. Сообщество Голанг предлагает новые имена шаблонов.

Ответ 9

Если вы хотите эмулировать синтаксис ___.new() вы можете сделать что-то вроде:

type Thing struct {
    Name string
    Num int
}
type Constructor_Thing struct {}
func (c CThing) new(<<CONSTRUCTOR ARGS>>) Thing {
  var thing Thing
  //initiate thing from constructor args
  return thing
}
var cThing CThing

func main(){
  var myThing Thing
  myThing = cThing.new(<<CONSTRUCTOR ARGS>>)
  //...
}

Конечно, жаль, что Thing.new() не может быть реализован без реализации CThing.new() (iirc), что немного обидно...