Встраивание нескольких структур
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 37 из 107.
Go позволяет встраивать несколько структур в одну структуру, объединяя поведение из разных источников. Это похоже на множественное наследование в других языках, но реализовано с помощью подхода композиции в Go.
type Logger struct{}
func (l Logger) Log(msg string) string {
return "LOG: " + msg
}
type Notifier struct{}
func (n Notifier) Notify(msg string) string {
return "NOTIFY: " + msg
}
type Service struct {
Name string
Logger
Notifier
}
Структура Service теперь имеет доступ к методам обоих встроенных типов:
func main() {
s := Service{Name: "OrderService"}
fmt.Println(s.Log("started")) // LOG: запущен
fmt.Println(s.Notify("new order")) // NOTIFY: новый заказ
}
При встраивании нескольких структур может возникнуть конфликт имен, если у двух встроенных типов есть методы с одинаковыми именами. Go не разрешает эту неоднозначность автоматически:
type A struct{}
func (A) Greet() string { return "Hello from A" }
type B struct{}
func (B) Greet() string { return "Hello from B" }
type Combined struct {
A
B
}
func main() {
c := Combined{}
// c.Greet() - ошибка компиляции: неоднозначный селектор
fmt.Println(c.A.Greet()) // Hello from A
fmt.Println(c.B.Greet()) // Hello from B
}
Когда возникают конфликты, необходимо explicitly указать, method какого embedded типа вы хотите call, используя имя типа в качестве квалификатора.
Задание
ЛегкоДавайте создадим систему устройства умного дома, которая объединяет несколько возможностей с помощью встраивания структур. Вы создадите устройство, способное одновременно управлять освещением и воспроизводить музыку посредством встраивания отдельных типов контроллеров.
Вы организуете код в трёх файлах:
controllers.go: Create две независимые структуры контроллеров, предоставляющие разные возможности:LightControllerс полемBrightness(int) и methodSetLight(level int) string, который возвращаетLight set to [level]%AudioControllerс полемVolume(int) и methodSetVolume(level int) string, который возвращаетVolume set to [level]%
Status() string. Это создаёт конфликт naming, который вам потребуется обработать.LightController.Status()должен возвращатьBrightness: [Brightness]%, аAudioController.Status()—Volume: [Volume]%.device.go: Create структуруSmartDeviceс полемName, которая встраивает иLightController, иAudioController. Добавьте method с именемFullStatus() string, который возвращает имя устройства вместе со статусами обоих контроллеров, устраняя ambiguous посредством явного вызова methodStatus()каждого embedded типа.main.go: Считайте конфигурацию устройства из input, создайтеSmartDevice, используйте promoted methods для установки уровней освещения и громкости, затем выведите полный статус.
Будут предоставлены следующие входные данные:
- Строка 1: имя устройства
- Строка 2: initial уровень яркости (целое число)
- Строка 3: initial уровень громкости (целое число)
- Строка 4: новый уровень яркости для установки (целое число)
- Строка 5: новый уровень громкости для установки (целое число)
Ваш method FullStatus() должен возвращать:
[Name] - [LightController.Status()], [AudioController.Status()]Выведите результаты вызова SetLight и SetVolume (которые являются promoted из embedded типов), затем выведите полный статус.
Например, если заданы Living Room Hub, 50, 30, 75 и 60, результат должен быть следующим:
Light set to 75%
Volume set to 60%
Living Room Hub - Brightness: 75%, Volume: 60%Обратите внимание: SetLight и SetVolume напрямую доступны в SmartDevice благодаря продвижению methods, но для Status() требуется явная квалификация, поскольку оба embedded типа имеют этот method.
Попробуйте сами
package main
import (
"fmt"
)
func main() {
// Прочитать ввод
var name string
fmt.Scanln(&name)
var initialBrightness int
fmt.Scanln(&initialBrightness)
var initialVolume int
fmt.Scanln(&initialVolume)
var newBrightness int
fmt.Scanln(&newBrightness)
var newVolume int
fmt.Scanln(&newVolume)
// TODO: Создайте SmartDevice с заданным именем и начальными значениями
// TODO: Используйте продвинутый метод SetLight и выведите результат
// TODO: Используйте продвинутый метод SetVolume и выведите результат
// TODO: Выведите полный статус с помощью метода FullStatus()
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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