Встроенные типы (например, мьютексы) должны находиться в верхней части списка полей структуры, и должна быть пустая строка, отделяющая встроенные поля от обычных полей.
Менее удачный пример:
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: объявления переменных верхнего уровня
 

Комментариев нет:
Отправить комментарий