Перейти до основного вмісту
Модуль 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 на (рядок імені) (*Користувач, помилка): if name == '' повертає nil, errors.New('name obbligatorio').

Завантаження редактора…
Показати підказку

Якщо є передумови, поверніть (*T, помилка) і поверніть нуль + помилку в разі невдачі.

Рішення доступне після 3 спроб

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

Що таке ідіоматична конвенція для конструкторів у Go?

Go
// Which signature is more idiomatic?
Варіанти відповідей

Підведення підсумків

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