Menu
CoddyTech

Встраивание нескольких структур

Часть раздела Объектно-ориентированное программирование путешествия по 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
}

При возникновении конфликтов необходимо явно указать, какой метод встроенного типа вы хотите вызвать, используя имя типа в качестве квалификатора.

challenge icon

Задание

Легко

Давайте создадим систему устройств для умного дома, объединяющую несколько возможностей с помощью встраивания структур. Вы создадите устройство, которое может управлять освещением и воспроизводить музыку, встраивая отдельные типы контроллеров.

Вы распределите код по трём файлам:

  • 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()
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Go