Проектирование интерфейсов
Аня оставила на ревью развёрнутый комментарий: контракт описывает тот, кто использует, а не производитель. Миша переделал слой хранилища по её схеме, и обработчики избавились от лишних проверок типов.
интерфейсы в Go делают маленькими и объявляют у потребителя — тот, кто использует значение, описывает, что ему нужно, а не производитель навязывает свой контракт.
Маленькие интерфейсы
Что здесь показано
- Стек — локальные переменные функции.
i = 3— значение;p = ptr → 10— указатель на значение;b = "hello"— срез (заголовок: указатель + длина + ёмкость). - Куча — данные, на которые ссылаются переменные:
rodata— байты строки-литерала (в бинарнике),данные N Б— бэкинг-массив слайса,строка— динамическая строка. - Горутины — лёгкие потоки Go: статус (running/runnable/waiting…), функция, в которой каждая сейчас находится, размер стека (растёт по мере работы) и пометка «из main» — горутина запущена из твоего кода.
- GC — сборщик мусора: фаза (off/mark) и число завершённых циклов.
Стандартная библиотека построена на однодвухметодных интерфейсах. Маленький интерфейс легко удовлетворить, легко имитировать в тестах, и его назначение видно из имени. Большой интерфейс с десятком методов подходит одному типу и бесполезен остальным.
Объявлять у потребителя
// service.go — в пакете, который ИСПОЛЬЗУЕТ данные
type repository interface {
FindFile(id string) (FileMeta, error)
}
type Service struct {
repo repository
}
Интерфейс объявляется там, где его используют, а не рядом с реализацией. Потребитель описывает минимальный контракт, который ему нужен — и любая реализация, подходящая под него, работает без изменения кода производителя. Это переворачивает привычную иерархию классов: не «класс объявляет, что он умеет», а «потребитель объявляет, что ему нужно». Так Аня предложила поступить с хранилищем: контракт описывает тот, кто хранилище использует.
Принимай интерфейсы, возвращай конкретные типы
Функции принимают интерфейсы (гибкость: работает с любой реализацией) и возвращают конкретные типы (вызывающий получает полный набор возможностей и не нуждается в type assertion). Возвращать интерфейс стоит только когда реально нужно скрыть реализацию.
Вы узнаете:
- как переход на generics заменяет
interface{}в сигнатурах - как встраивание интерфейсов собирает
ReadWriter
Проверьте себя
Когда интерфейс мешает, а не помогает?
Какой принцип иллюстрирует io.Copy(dst io.Writer, src io.Reader)?
Почему strings.ToUpper(s string) string принимает конкретный string, а не интерфейс?
правильных ответов
Интерфейсы: маленькие, у потребителя, принимаются функциями, а возвращаются конкретные типы. Это и есть идиоматичный Go.
10 000 ₽ · 60 минут · онлайн