Введение в интерфейсы
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 25 из 107.
Интерфейс (interface) в Go определяет набор сигнатур методов без их реализации. Он описывает, что тип может делать, а не то, как он это делает. Любой тип, который реализует все методы интерфейса, автоматически удовлетворяет этому интерфейсу.
Вот как определяется интерфейс:
type Speaker interface {
Speak() string
}Этот интерфейс Speaker требует, чтобы любой реализующий его тип имел метод Speak(), который возвращает строку. Теперь любую структуру с этим методом можно использовать везде, где ожидается Speaker.
type Dog struct {
Name string
}
func (d Dog) Speak() string {
return d.Name + " says woof!"
}
type Cat struct {
Name string
}
func (c Cat) Speak() string {
return c.Name + " says meow!"
}Оба типа Dog и Cat удовлетворяют интерфейсу Speaker, так как каждый из них имеет метод Speak() с правильной сигнатурой. Теперь вы можете писать функции, которые принимают любой Speaker:
func MakeSound(s Speaker) {
fmt.Println(s.Speak())
}
func main() {
dog := Dog{Name: "Buddy"}
cat := Cat{Name: "Whiskers"}
MakeSound(dog) // Buddy говорит woof!
MakeSound(cat) // Whiskers говорит meow!
}Интерфейсы позволяют писать гибкий, переиспользуемый код, который работает с любым типом, соответствующим требованиям интерфейса, независимо от базовой реализации.
Задание
ЛегкоДавайте создадим систему описания транспортных средств, которая демонстрирует, как интерфейсы позволяют различным типам разделять общее поведение. Вы создадите интерфейс, которому могут соответствовать несколько типов транспортных средств, а затем будете использовать его для единообразного описания любого транспортного средства.
Вы организуете свой код в двух файлах:
vehicles.go: Определите интерфейсDescriberс единственным методомDescribe() string. Затем создайте две структуры, которые удовлетворяют этому интерфейсу:Carс полямиBrandиModel(оба типа string)Bicycleс полямиType(string) иGears(int)
Describe(), который возвращает строку с описанием транспортного средства.main.go: Создайте функцию с именемPrintDescription, которая принимает любойDescriberи выводит результат вызова его методаDescribe(). Считайте информацию о транспортных средствах из входных данных, создайте объекты Car и Bicycle и используйте свою функцию для вывода описания каждого из них.
Будут предоставлены следующие входные данные:
- Строка 1: Марка автомобиля (Car brand)
- Строка 2: Модель автомобиля (Car model)
- Строка 3: Тип велосипеда (Bicycle type)
- Строка 4: Количество передач (целое число)
Ваши методы Describe() должны возвращать строки в следующих форматах:
- Car:
[Brand] [Model] - Bicycle:
[Type] bike with [Gears] gears
Например, для входных данных Toyota, Camry, Mountain и 21 ваш вывод должен быть следующим:
Toyota Camry
Mountain bike with 21 gearsКлючевая идея заключается в том, что ваша функция PrintDescription работает с любым типом, имеющим метод Describe() — ей не нужно знать, имеет ли она дело с Car, Bicycle или любым другим типом транспортного средства, который вы можете добавить позже.
Шпаргалка
Интерфейс в Go определяет набор сигнатур методов без их реализации. Любой тип, реализующий все эти методы, автоматически удовлетворяет этому интерфейсу.
Определение интерфейса:
type Speaker interface {
Speak() string
}Типы, реализующие необходимые методы, удовлетворяют интерфейсу:
type Dog struct {
Name string
}
func (d Dog) Speak() string {
return d.Name + " says woof!"
}
type Cat struct {
Name string
}
func (c Cat) Speak() string {
return c.Name + " says meow!"
}Функции могут принимать любой тип, удовлетворяющий интерфейсу:
func MakeSound(s Speaker) {
fmt.Println(s.Speak())
}
func main() {
dog := Dog{Name: "Buddy"}
cat := Cat{Name: "Whiskers"}
MakeSound(dog) // Buddy says woof!
MakeSound(cat) // Whiskers says meow!
}Попробуйте сами
package main
import (
"fmt"
)
// TODO: Создайте функцию PrintDescription, которая принимает любой Describer
// и выводит результат вызова его метода Describe()
func main() {
// Чтение входных данных
var carBrand string
var carModel string
var bicycleType string
var gears int
fmt.Scanln(&carBrand)
fmt.Scanln(&carModel)
fmt.Scanln(&bicycleType)
fmt.Scanln(&gears)
// TODO: Создайте Car с маркой и моделью из входных данных
// TODO: Создайте Bicycle с типом и количеством передач из входных данных
// TODO: Используйте PrintDescription для вывода описания автомобиля
// TODO: Используйте PrintDescription для вывода описания велосипеда
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker Pool