В Go полиморфизм строится не на наследовании, а на интерфейсах. Это один из ключевых принципов языка: код зависит не от конкретной реализации, а от поведения.
Что такое интерфейс в Go
Интерфейс — это набор методов. Если тип реализует все методы интерфейса, он автоматически считается его реализацией. Явно писать implements не нужно.
type Speaker interface {
Speak() string
}
type Dog struct{}
func (d Dog) Speak() string { return "Woof" }
type Cat struct{}
func (c Cat) Speak() string { return "Meow" }
И Dog, и Cat подходят под Speaker, потому что у них есть метод Speak().
Как здесь работает полиморфизм
Полиморфизм — это возможность работать с разными типами через один общий интерфейс.
func SaySomething(s Speaker) {
fmt.Println(s.Speak())
}
Теперь функция SaySomething не знает, кто именно передан: собака, кошка или любой другой тип. Главное — чтобы он умел Speak().
Это даёт:
- гибкость архитектуры
- слабую связанность кода
- простую замену реализаций
- удобное тестирование через моки 🧪
Почему это удобно в реальных проектах
Например, есть сервис отправки уведомлений:
type Notifier interface {
Send(message string) error
}
Реализации могут быть разными:
- EmailNotifier
- TelegramNotifier
- SMSNotifier
Основной код работает только с Notifier, а не с конкретным каналом доставки. Это упрощает масштабирование и поддержку проекта 🚀
Пустой интерфейс и any
Раньше для хранения значения любого типа использовали interface{}. Сейчас для этого есть алиас any.
func Print(value any) {
fmt.Println(value)
}
Но применять any стоит осторожно: он снижает типобезопасность и часто делает код менее понятным.
Важный нюанс: interface и nil
Одна из частых ловушек в Go — интерфейс может быть не равен nil, даже если внутри лежит nil-указатель. Это происходит потому, что интерфейс хранит:
- конкретный тип
- конкретное значение
Если тип известен, интерфейс уже не считается nil ⚠️
Когда использовать интерфейсы
Интерфейсы полезны, если:
- есть несколько реализаций одного поведения
- нужен чистый и расширяемый API
- важна подмена зависимостей в тестах
- хочется уменьшить связанность между пакетами
Но не стоит создавать интерфейсы “на будущее”. В Go хорошая практика — объявлять интерфейс там, где он нужен потребителю, а не производителю.
Итог
Интерфейсы в Go — это основа полиморфизма без сложной иерархии классов. Они позволяют писать простой, гибкий и поддерживаемый код. Главное правило: описывайте не сущности, а действия, которые от них ожидаете 🔧
Подборку полезных каналов про IT — стоит посмотреть в закрепе/описании канала 📚