Menu
Coddy logo textTech

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

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

challenge icon

Задание

Легко

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

Вы организуете код в трёх файлах:

  • controllers.go: Create две независимые структуры контроллеров, предоставляющие разные возможности:
    • LightController с полем Brightness (int) и method SetLight(level int) string, который возвращает Light set to [level]%
    • AudioController с полем Volume (int) и method SetVolume(level int) string, который возвращает Volume set to [level]%
    Оба контроллера также должны иметь method Status() string. Это создаёт конфликт naming, который вам потребуется обработать. LightController.Status() должен возвращать Brightness: [Brightness]%, а AudioController.Status()Volume: [Volume]%.
  • device.go: Create структуру SmartDevice с полем Name, которая встраивает и LightController, и AudioController. Добавьте method с именем FullStatus() string, который возвращает имя устройства вместе со статусами обоих контроллеров, устраняя ambiguous посредством явного вызова method Status() каждого 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()
}
quiz iconПроверьте себя

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

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

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