Перейти к основному содержимому
Модуль 5 · Урок 5 из 525/50 в курсе~10 min
Уроки модуля (5/5)

Шаблон конструктора `New...`

В Go нет ключевого слова constructor. Конвенция — это фабрика функция, которая возвращает правильно инициализированное значение или указатель. Автор имя обычно называется NewT (или NewXxx).

Базовый узор: NewT

Go
type User struct {
    Name string
    Age  int
}

func NewUser(name string, age int) *User {
    return &User{Name: name, Age: age}
}

u := NewUser("Ada", 36)
fmt.Println(u.Name)

Преимущества по сравнению с прямым литералом &User{...}:

  • Вы централизуете проверку и настройки по умолчанию. — Вы можете иметь неэкспортированные поля и заполнять их только из конструктора.
  • Вы можете изменить реализацию, не ломая вызывающие программы.

Конструктор с проверкой: (*T, ошибка)

Если инициализация может завершиться неудачей, верните (*T, error):

Go
import "errors"

func NewUser(name string, age int) (*User, error) {
    if name == "" {
        return nil, errors.New("nome obbligatorio")
    }
    if age < 0 {
        return nil, errors.New("eta non valida")
    }
    return &User{Name: name, Age: age}, nil
}

u, err := NewUser("Ada", 36)
if err != nil {
    // handle...
}

Это идиоматический шаблон для любого конструктора с предварительные условия: соединение с БД, дескриптор файла, конфигурация сервера и т. д.

Несколько конструкторов

Если у вас есть несколько «способов» создания, используйте описательные имена:

Go
func NewUser(name string) *User { ... }
func NewUserFromJSON(data []byte) (*User, error) { ... }
func NewAdmin() *User { ... }

Никакой перегрузки в Go: имя устраняет неоднозначность.

Функциональные возможности (расширенный обзор)

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

Go
type Option func(*Server)

func WithPort(p int) Option {
    return func(s *Server) { s.Port = p }
}

func WithTLS(cert, key string) Option {
    return func(s *Server) { s.Cert, s.Key = cert, key }
}

func NewServer(opts ...Option) *Server {
    s := &Server{Port: 8080} // default
    for _, opt := range opts {
        opt(s)
    }
    return s
}

s := NewServer(WithPort(9000), WithTLS("c.pem", "k.pem"))

Он более многословен, чем литерал, но хорошо масштабируется со многими дополнительными параметрами. параметры и сохраняет обратную совместимость (добавление нового Option не прерывает существующие абоненты).

Когда вам НЕ нужен конструктор

Если нулевое значение типа уже полезно, позвольте вызывающему объекту использовать прямой буквал. Канонический пример: sync.Mutex не имеет конструктора, var m sync.Mutex — это уже готовый к использованию мьютекс. То же самое для bytes.Buffer.

Правило такое: «Сделайте нулевое значение полезным». Если можешь, нет обязательный конструктор.

Попробуйте это

Упражнение#go.m5.l5.e1
Попыток: 0Загрузка…

Напишите фабричную функцию NewUser(name string, age int) *User, которая возвращает &User{Name: name, Age: age}.

Загрузка редактора…
Показать подсказку

Используйте литерал `&User{...}` для возврата указателя.

Решение доступно после 3 попыток

Упражнение#go.m5.l5.e2
Попыток: 0Загрузка…

Превратить NewUser в (строку имени) (*User, error): если name == '' вернуть ноль, error.New('nome obbligatorio').

Загрузка редактора…
Показать подсказку

Если есть предварительные условия, верните (*T, ошибка) и верните ноль + ошибка в случае неудачи.

Решение доступно после 3 попыток

Quiz#go.m5.l5.e3
Готово

Каково идиоматическое соглашение для конструкторов в Go?

Go
// Which signature is more idiomatic?
Варианты ответа

Резюме

  • Нет constructor: используйте функцию NewT (или New внутри одноименного пакета).
  • Верните *T для простого случая; (*T, error) с предварительными условиями или проверкой.
  • При ошибке: вернуть nil в качестве указателя и пояснительную ошибку в качестве второго значения.
  • Никаких перегрузок; используйте описательные имена (NewUserFromJSON, NewAdmin). — «Сделайте нулевое значение полезным»: если можете, то конструктор не нужен.
  • Расширенный шаблон для многих дополнительных параметров: функциональные параметры.