Продвижение методов
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 36 из 107.
Так же, как встраивание struct продвигает поля во внешнюю struct, Go также продвигает методы встроенных типов. Это означает, что вы можете вызывать методы, определённые во встроенной struct, напрямую у внешней struct.
type Engine struct {
Horsepower int
}
func (e Engine) Start() string {
return "Engine started"
}
type Car struct {
Model string
Engine // встроенный
}
Поскольку Car встраивает Engine, метод Start() автоматически продвигается:
func main() {
c := Car{
Model: "Sedan",
Engine: Engine{Horsepower: 200},
}
fmt.Println(c.Start()) // Двигатель запущен
fmt.Println(c.Engine.Start()) // тоже работает
}
Это продвижение имеет важное следствие для интерфейсов. Если встроенный тип satisfies interface, внешний тип автоматически satisfies его тоже:
type Starter interface {
Start() string
}
func Ignite(s Starter) {
fmt.Println(s.Start())
}
func main() {
c := Car{Model: "Sedan", Engine: Engine{Horsepower: 200}}
Ignite(c) // Car удовлетворяет Starter через Engine
}
Тип Car никогда явно не реализует Starter, однако удовлетворяет интерфейсу, поскольку встроенный Engine имеет требуемый метод. Так Go обеспечивает повторное использование поведения без традиционного наследования.
Задание
ЛегкоДавайте создадим систему музыкального проигрывателя, которая демонстрирует, как method встроенных типов продвигаются во внешний тип и как это позволяет обеспечить соответствие interface посредством композиции.
Вы организуете код в двух файлах:
audio.go: создайте основные компоненты музыкального проигрывателя:- структуру
AudioPlayerсBrandfieldтипа string иPlay() stringmethod, который возвращает[Brand] playing audio Playableinterface, requiringPlay() stringmethod- структуру
SmartphoneсModelfield, которая встраиваетAudioPlayer - структуру
TabletсModelfield, которая встраиваетAudioPlayer
- структуру
main.go: создайтеfunctionс именемStartPlayback, котораяacceptsлюбойPlayableи возвращаетresultвызоваPlay(). Считайте информацию об устройствах из ввода, создайте Smartphone и Tablet и продемонстрируйте, что оба удовлетворяютPlayableinterfaceблагодаря встроенномуAudioPlayer, хотя ни один из типов явно не реализуетPlay()самостоятельно.
Будут предоставлены следующие входные данные:
- Строка 1: название модели Smartphone
- Строка 2: аудиобренд Smartphone
- Строка 3: название модели Tablet
- Строка 4: аудиобренд Tablet
Для каждого устройства выведите его модель, а затем result передачи устройства в StartPlayback.
Например, если заданы iPhone 15, Apple Audio, iPad Pro и Beats, ваши выходные данные должны быть такими:
iPhone 15
Apple Audio playing audio
iPad Pro
Beats playing audioОбратите внимание, что и Smartphone, и Tablet можно передать в StartPlayback, поскольку Play() method из AudioPlayer автоматически продвигается в каждый внешний тип, обеспечивая соответствие Playable interface без какого-либо дополнительного кода.
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
)
// TODO: Создать функцию StartPlayback, которая принимает любой Playable
// и возвращает результат вызова Play()
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Прочитать модель смартфона
scanner.Scan()
smartphoneModel := scanner.Text()
// Прочитать аудио-бренд смартфона
scanner.Scan()
smartphoneBrand := scanner.Text()
// Прочитать модель планшета
scanner.Scan()
tabletModel := scanner.Text()
// Прочитать аудио-бренд планшета
scanner.Scan()
tabletBrand := scanner.Text()
// TODO: Создать Smartphone со встроенным AudioPlayer
// TODO: Создать Tablet со встроенным AudioPlayer
// TODO: Вывести модель смартфона, затем вызвать StartPlayback со смартфоном
// TODO: Вывести модель планшета, затем вызвать StartPlayback с планшетом
_ = smartphoneModel
_ = smartphoneBrand
_ = tabletModel
_ = tabletBrand
fmt.Println("TODO: Complete the implementation")
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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Потренируйтесь самостоятельно: Онлайн-компилятор Go