Menu
Coddy logo textTech

Делегирование

Часть раздела Object Oriented Programming путешествия по Lua на Coddy — урок 50 из 70.

Хранение объекта внутри другого объекта — это только первый шаг. Настоящая мощь композиции проявляется в делегировании — когда внешний объект перенаправляет вызовы методов своим внутренним компонентам.

Представьте себе процесс запуска автомобиля. Вы не лезете под капот, чтобы вручную раскрутить двигатель. Вместо этого вы поворачиваете ключ, а всё остальное машина делает сама внутри.

В коде это означает, что класс Car предоставляет простой метод :start(), который делегирует фактическую работу двигателю:

function Car:start()
    print("Turning the key...")
    self.engine:run()  -- Делегирование внутреннему объекту
end

Машине не нужно знать как работает двигатель — она просто приказывает двигателю выполнять свою работу. Это позволяет разделять зоны ответственности. Класс Engine отвечает за логику двигателя, в то время как Car координирует остальные части:

function Engine:run()
    print("Engine running at " .. self.horsepower .. " HP")
end

local engine = Engine:new(250)
local car = Car:new("Coupe", engine)
car:start()
-- Вывод:
-- Поворот ключа...
-- Двигатель работает на мощности 250 л.с.

Делегирование создает чистый интерфейс для пользователей вашего класса. Они взаимодействуют с автомобилем, а не с его внутренними частями. Если вы позже замените реализацию двигателя на другую, публичный интерфейс автомобиля останется прежним — изменится только внутреннее делегирование.

challenge icon

Задание

Легко

Давайте создадим систему музыкального плеера, которая демонстрирует делегирование! Вы создадите компонент Speaker и MusicPlayer, который его содержит. Плеер не будет напрямую заниматься выводом звука — вместо этого он делегирует эту задачу своему внутреннему динамику.

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

  • Speaker.lua: Создайте класс Speaker с конструктором :new(volume), который сохраняет уровень громкости. Добавьте метод :playSound(trackName), который выводит: Playing "{trackName}" at volume {volume}
  • MusicPlayer.lua: Создайте класс MusicPlayer, чей конструктор :new(brand, speaker) принимает название бренда и объект Speaker. Плеер сохраняет динамик внутри себя. Добавьте следующие методы:
    • :play(trackName) — выводит {brand} player starting..., затем делегирует выполнение методу динамика :playSound()
    • :stop() — выводит {brand} player stopped
  • main.lua: Соберите все вместе! Считайте входные данные для громкости динамика, бренда плеера и названия трека. Сначала создайте Speaker, затем передайте его в конструктор MusicPlayer. Вызовите метод плеера :play() с названием трека, а затем вызовите :stop().

Вы получите три входных значения:

  1. Громкость динамика (число)
  2. Бренд музыкального плеера (строка)
  3. Название трека для воспроизведения (строка)

Ваш вывод должен показывать, как плеер взаимодействует со своим динамиком:

{brand} player starting...
Playing "{trackName}" at volume {volume}
{brand} player stopped

Например, если входные данные — 75, SoundMax и Summer Vibes, вывод должен быть следующим:

SoundMax player starting...
Playing "Summer Vibes" at volume 75
SoundMax player stopped

Обратите внимание, что MusicPlayer не знает, как воспроизводить звук — он просто дает команду своему динамику выполнить эту часть. Это и есть делегирование в действии: внешний объект предоставляет чистый интерфейс, в то время как внутренний компонент выполняет специализированную работу!

Шпаргалка

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

Внешний объект предоставляет простые методы, которые делегируют работу внутренним объектам:

function Car:start()
    print("Turning the key...")
    self.engine:run()  -- Делегирование внутреннему объекту
end

Внутренний объект обрабатывает свою собственную специализированную логику:

function Engine:run()
    print("Engine running at " .. self.horsepower .. " HP")
end

local engine = Engine:new(250)
local car = Car:new("Coupe", engine)
car:start()
-- Output:
-- Turning the key...
-- Engine running at 250 HP

Преимущества делегирования:

  • Внешний класс не обязан знать, как работает внутренний объект
  • Пользователи взаимодействуют с чистым публичным интерфейсом
  • Внутренние реализации можно заменять без изменения публичного интерфейса

Попробуйте сами

-- main.lua
-- Объединяем всё вместе!

local Speaker = require('Speaker')
local MusicPlayer = require('MusicPlayer')

-- Чтение входных данных
local volume = tonumber(io.read())
local brand = io.read()
local trackName = io.read()

-- TODO: Создать Speaker с заданным уровнем громкости

-- TODO: Создать MusicPlayer с брендом и динамиком

-- TODO: Вызвать метод play проигрывателя с названием трека

-- TODO: Вызвать метод stop проигрывателя
quiz iconПроверьте себя

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

Все уроки раздела Object Oriented Programming