Ключевой критерий это количество методов в поведении. Если нужно абстрагироваться над одним действием, берите функцию-тип. Если поведение требует нескольких связанных методов — интерфейс.
Интерфейс оправдан, когда поведение требует нескольких связанных методов или когда реализация несёт внутреннее состояние — соединение, кэш, буфер. Тогда контракт нужно зафиксировать явно, и интерфейс делает это лучше.
Показательный пример из стандартной библиотеки —
net/http. Там оба подхода живут рядом: •
http.Handler — интерфейс с одним методом ServeHTTP•
http.HandlerFunc — функциональный тип, который реализует этот же интерфейс. Это позволяет передавать как структуры со состоянием, так и простые функции, не заставляя автора каждый раз писать обёртку.
🐸 Библиотека Go для собеса