понедельник, 21 декабря 2020 г.

Go style guides: встраивание в структуры

Встроенные типы (например, мьютексы) должны находиться в верхней части списка полей структуры, и должна быть пустая строка, отделяющая встроенные поля от обычных полей.

Менее удачный пример:

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
}


Читайте также:


Комментариев нет:

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