Интерфейс — контракт
У «Полки» появился второй способ хранения файлов — облачный S3. Сергей предложил общий договор Storage, и Миша написал две реализации: локальный диск и облако. Тесты переехали на заглушку.
интерфейс описывает поведение, а не данные; тип удовлетворяет интерфейсу неявно — если у него есть все методы с нужными сигнатурами, без всяких объявлений.
Контракт
type Storage interface {
Save(name string, data []byte) error
}
type LocalDisk struct{}
func (LocalDisk) Save(name string, data []byte) error {
// пишет файл на локальный диск
return nil
}
type S3Backend struct{}
func (S3Backend) Save(name string, data []byte) error {
// кладёт объект в облако
return nil
}
var st Storage = LocalDisk{}
Интерфейс — это контракт: «у меня есть метод Save с такой сигнатурой». Кто его выполняет — локальный диск или облако — неважно. Это про поведение, а не про данные: интерфейс не может содержать поля.
Неявная реализация
В Go нет слова implements. Тип удовлетворяет интерфейсу автоматически, если его method set покрывает все методы интерфейса:
Что здесь показано
- Стек — локальные переменные функции.
i = 3— значение;p = ptr → 10— указатель на значение;b = "hello"— срез (заголовок: указатель + длина + ёмкость). - Куча — данные, на которые ссылаются переменные:
rodata— байты строки-литерала (в бинарнике),данные N Б— бэкинг-массив слайса,строка— динамическая строка. - Горутины — лёгкие потоки Go: статус (running/runnable/waiting…), функция, в которой каждая сейчас находится, размер стека (растёт по мере работы) и пометка «из main» — горутина запущена из твоего кода.
- GC — сборщик мусора: фаза (off/mark) и число завершённых циклов.
Это ключевое отличие от языков с явной реализацией: интерфейс можно ввести задним числом, и существующие типы «подойдут» под него без правок. Стандартная библиотека так и устроена: io.Reader, io.Writer, fmt.Stringer — маленькие интерфейсы, которым удовлетворяют тысячи типов по всему коду. Так же получилось в «Полке»: LocalDisk написан давно и ничего не знал про Storage, но подошёл под него без единой правки.
Интерфейс принимает реализацию
Функция, принимающая интерфейс, работает с любым типом, выполняющим контракт:
func persist(st Storage, name string, data []byte) error {
return st.Save(name, data)
}
persist(LocalDisk{}, "report.pdf", data)
persist(S3Backend{}, "report.pdf", data)
Вы узнаете:
- что даёт пустой интерфейс
any - как интерфейс помогает делать моки в тестах
Проверьте себя
Как тип удовлетворяет интерфейсу в Go?
Что может содержать интерфейс?
Можно ли ввести интерфейс задним числом для уже написанного типа?
правильных ответов
Интерфейс — контракт о методах. Реализация неявная: тип подходит, если сигнатуры сошлись. Интерфейс принимает любую реализацию — отсюда гибкость и тестируемость кода.
10 000 ₽ · 60 минут · онлайн