Встроенные типы (например, мьютексы) должны находиться в верхней части списка полей структуры, и должна быть пустая строка, отделяющая встроенные поля от обычных полей.
Менее удачный пример:
type Client struct {
version int
http.Client
}
Более удачный пример:
type Client struct {
http.Client
version int
}
Встраивание должно приносить ощутимые преимущества, например, добавление или расширение функциональности семантически подходящим способом. Оно должно делать это с нулевым негативным воздействием на пользователя (избегайте встраивания типов в общедоступные структуры).
Встраивание не должно:
- Быть чисто косметическим или ориентированным на удобство.
- Делать внешние типы более сложными для создания или использования.
- Влиять на нулевые значения внешних типов. Если внешний тип имеет полезное нулевое значение, он все равно должен иметь полезное нулевое значение после внедрения внутреннего типа.
- Предоставлять несвязанные функции или поля внешнего типа в качестве побочного эффекта внедрения внутреннего типа.
- Выставлять неэкспортированные типы.
- Влиять на семантику копирования внешних типов.
- Изменять API внешнего типа или семантику типа.
- Вставлять неканоническую форму внутреннего типа.
- Раскрывать детали реализации внешнего типа.
- Разрешать пользователям наблюдать или контролировать внутренние элементы типа.
- Изменять общее поведение внутренних функций с помощью обертки таким образом, чтобы это могло удивить пользователей.
Проще говоря, внедряйте сознательно и намеренно. Хорошая лакмусовая бумажка: "будут ли все эти экспортированные внутренние методы/поля добавлены непосредственно во внешний тип"; если ответ - "некоторые" или "нет", не вставляйте внутренний тип - вместо этого используйте поле.
Менее удачный пример:
type A struct {
// Неудачно:
// A.Lock() и A.Unlock() теперь доступны,
// не предоставляет функциональной выгоды,
// разрешает пользователям контролировать
// детали внутреннего устройства A.
sync.Mutex
}
Более удачный пример:
type countingWriteCloser struct {
// Хорошо: функция Write() предоставлена
// во внешнем слое для определенной
// цели, а делегаты работают
// с Write() внутреннего типа.
io.WriteCloser
count int
}
func (w *countingWriteCloser) Write(bs []byte) (int, error) {
w.count += len(bs)
return w.WriteCloser.Write(bs)
}
Менее удачный пример:
type Book struct {
// Плохо: указатель
// изменяет полезность нулевого значения
io.ReadWriter
// другие поля
}
// позже
var b Book
b.Read(...) // panic: nil pointer
b.String() // panic: nil pointer
b.Write(...) // panic: nil pointer
Более удачный пример:
type Book struct {
// Хорошо: имеет полезное нулевое значение
bytes.Buffer
// другие поля
}
// позже
var b Book
b.Read(...) // ok
b.String() // ok
b.Write(...) // ok
Менее удачный пример:
type Client struct {
sync.Mutex
sync.WaitGroup
bytes.Buffer
url.URL
}
Более удачный пример:
type Client struct {
mtx sync.Mutex
wg sync.WaitGroup
buf bytes.Buffer
url url.URL
}
Читайте также:
- Go style guides: используйте префикс _ для неэкспортируемых глобальных переменных
- Go style guides: уменьшайте вложенность
- Go style guides: объявления переменных верхнего уровня
Комментариев нет:
Отправить комментарий