Модульні уроки (5/5)
Шаблон конструктора `New...`
У Go немає ключового слова constructor. Конвенція - це фабрика
функція, яка повертає правильно ініціалізоване значення або покажчик. за
ім’я, яке зазвичай називається NewT (або NewXxx).
Базовий візерунок: NewT
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):
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...
}Це ідіоматичний шаблон для будь-якого конструктора передумови: підключення до бази даних, дескриптор файлу, конфігурація сервера тощо.
Кілька конструкторів
Якщо у вас є кілька «способів» побудови, використовуйте описові назви:
func NewUser(name string) *User { ... }
func NewUserFromJSON(data []byte) (*User, error) { ... }
func NewAdmin() *User { ... }Жодного перевантаження в 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.
Правило таке: «Зробіть нульове значення корисним». Якщо можна, ні обов'язковий конструктор.
Спробуйте
Напишіть фабричну функцію NewUser(name string, age int) *User, яка повертає &User{Name: name, Age: age}.
Показати підказку
Використовуйте літерал `&User{...}`, щоб повернути покажчик.
Рішення доступне після 3 спроб
Перетворіть NewUser на (рядок імені) (*Користувач, помилка): if name == '' повертає nil, errors.New('name obbligatorio').
Показати підказку
Якщо є передумови, поверніть (*T, помилка) і поверніть нуль + помилку в разі невдачі.
Рішення доступне після 3 спроб
Що таке ідіоматична конвенція для конструкторів у Go?
// Which signature is more idiomatic?Підведення підсумків
- Ні
constructor: використовуйте функціюNewT(абоNewвсередині однойменного пакета). - Повернення
*Tдля простого випадку;(*T, error)з попередніми умовами або перевіркою. - У разі помилки: повертає
nilяк вказівник і пояснювальну помилку як друге значення. - Відсутність перевантажень; використовувати описові назви (
NewUserFromJSON,NewAdmin). - «Зробити нульове значення корисним»: якщо можна, конструктор непотрібний.
- Розширений шаблон для багатьох додаткових параметрів: функціональні параметри.