Интерфейсы

Тема дорожной карты · Golang

Интерфейсы в Go определяют набор сигнатур методов, которым должен неявно соответствовать любой конкретный тип, обеспечивая полиморфизм и слабую связность без явных объявлений implements. Стандартная библиотека построена вокруг небольших составных интерфейсов — io.Reader, io.Writer, http.Handler и fmt.Stringer, — которые Golang-программы свободно комбинируют для построения гибких абстракций. При написании тестируемого кода интерфейсы в Go позволяют заменять реальные зависимости (PostgreSQL-клиент на pgx или sqlx) mock-реализациями, сгенерированными gomock или testify/mock, делая юнит-тесты чистыми и детерминированными. Пустой интерфейс any (псевдоним interface{}) может хранить значение любого типа, но следует использовать его экономно в пользу конкретных интерфейсов, выражающих намерение. Освоение интерфейсов в Go — ключ к идиоматичному Golang-дизайну: от определения цепочек http.Handler middleware до реализации обёрток над trace.Tracer из OpenTelemetry.

Как это работает

Интерфейсы покрывает встроенное: массивы (фиксированный размер — редко в идиоматичном Go), slices (динамические массивы — рабочая лошадка), maps (map[K]V, на хешах), strings (UTF-8 байтовые последовательности, неизменяемые). Struct — пользовательские типы с именованными полями; методы на типах (func (r Receiver) Method()); интерфейсы декларируют наборы методов (удовлетворяются неявно — нет ключевого слова implements). Указатели через & и *; slices и maps уже содержат указатели внутри.

Когда применять

Slices везде, где массивы соблазняют — почти всегда правильный ответ. Map — для lookup по ключу; помните, что Go-map не упорядочены. Embedding struct — для композиции (нет эквивалента наследования). Маленькие интерфейсы (1-3 метода) — Go-интерфейсы лучше когда обнаружены, чем объявлены (выносите, когда есть 2+ реализаций).

Типичные ошибки

Ловушки Интерфейсы: slice-граблик — append(s, x) может или не может делить underlying массив с s (при реалокации не делит — тихие баги); for _, v := range slice { go func() { use(v) }() } захватывает v по ссылке до 1.22 (closures видят последнее значение — починено в 1.22, но многие туториалы до этого); nil-maps паникуют на запись (make(map[K]V)); огромные interface-типы убивают идиому "implicitly satisfied".

Связанные понятия

Полезные ресурсы