Встраивание нескольких структур
Часть раздела Объектно-ориентированное программирование путешествия по 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()) // Привет от A
fmt.Println(c.B.Greet()) // Привет от B
}
При возникновении конфликтов необходимо явно указать, какой метод встроенного типа вы хотите вызвать, используя имя типа в качестве квалификатора.
Задание
ЛегкоДавайте создадим систему устройств для умного дома, объединяющую несколько возможностей с помощью встраивания структур. Вы создадите устройство, которое может управлять освещением и воспроизводить музыку, встраивая отдельные типы контроллеров.
Вы распределите код по трём файлам:
controllers.go: Создайте две независимые структуры контроллеров, предоставляющие разные возможности:LightControllerс полемBrightness(int) и методомSetLight(level int) string, который возвращаетLight set to [level]%AudioControllerс полемVolume(int) и методомSetVolume(level int) string, который возвращаетVolume set to [level]%
Status() string. Это создаёт конфликт имён, который вам нужно будет обработать.LightController.Status()должен возвращатьBrightness: [Brightness]%, аAudioController.Status()должен возвращатьVolume: [Volume]%.device.go: Создайте структуруSmartDeviceс полемName, в которую встроены оба типа:LightControllerиAudioController. Добавьте методFullStatus() string, который возвращает имя устройства вместе со статусами обоих контроллеров, устраняя неоднозначность посредством явного вызова методаStatus()каждого встроенного типа.main.go: Считайте конфигурацию устройства из входных данных, создайтеSmartDevice, используйте унаследованные методы для установки уровней освещения и громкости, а затем выведите полный статус.
Будут предоставлены следующие входные данные:
- Строка 1: Имя устройства
- Строка 2: Начальный уровень яркости (целое число)
- Строка 3: Начальный уровень громкости (целое число)
- Строка 4: Новый уровень яркости для установки (целое число)
- Строка 5: Новый уровень громкости для установки (целое число)
Ваш метод FullStatus() должен возвращать:
[Name] - [LightController.Status()], [AudioController.Status()]Выведите результаты вызовов SetLight и SetVolume (они унаследованы от встроенных типов), затем выведите полный статус.
Например, для входных данных Lounge, 50, 30, 75 и 60 ваш вывод должен быть таким:
Light set to 75%
Volume set to 60%
Lounge - Brightness: 75%, Volume: 60%Обратите внимание: методы SetLight и SetVolume напрямую доступны у SmartDevice благодаря продвижению методов, но для вызова Status() требуется явное указание типа, поскольку этот метод есть у обоих встроенных типов.
Попробуйте сами
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