Делегирование
Часть раздела 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 л.с.Делегирование создает чистый интерфейс для пользователей вашего класса. Они взаимодействуют с автомобилем, а не с его внутренними частями. Если вы позже замените реализацию двигателя на другую, публичный интерфейс автомобиля останется прежним — изменится только внутреннее делегирование.
Задание
ЛегкоДавайте создадим систему музыкального плеера, которая демонстрирует делегирование! Вы создадите компонент 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().
Вы получите три входных значения:
- Громкость динамика (число)
- Бренд музыкального плеера (строка)
- Название трека для воспроизведения (строка)
Ваш вывод должен показывать, как плеер взаимодействует со своим динамиком:
{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 проигрывателя
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Концепция 'self'
Таблицы с функциямиЯвный 'self'Синтаксис с двоеточиемТочка против двоеточияИтоги — Движущаяся точка4Проект: Цифровой банк
Настройка проектаМетод пополнения7Полиморфизм и переопределение
Переопределение методовВызов родительских методовУтиная типизацияОбщий интерфейсПроверка типаИтоги — Роли сотрудников2Паттерн прототипа класса
Концепция прототипаСвязывание через __indexКонструктор :new()Инициализация атрибутовНезависимые экземплярыИтоги — Автозавод5Перегрузка операторов в ООП
Сложение объектовВычитание объектовКонкатенация объектовСравнение объектов (<, >)Повторение — Математика кошелька8Инкапсуляция
Соглашения об именованииЗамыкания для приватностиДоступ через замыканияТаблицы только для чтенияЛогика валидацииИтоги — Защищенное хранилище11Паттерны проектирования (Lite)
Фабричные функцииТаблица SingletonПаттерн IteratorObserver (Слушатель)Итоги — Logger Factory3Состояние и поведение объектов
Переменные экземпляраГеттерыСеттерыВычисляемые свойстваФорматирование строкПроверка на равенствоПовторение — Оценка студента6Основы наследования
Настройка наследованияНаследование методовРасширение конструктораДобавление дочерних методовОбщее и уникальноеИтоги: Иерархия фигур9Композиция и примеси
Отношение "Has-A"ДелегированиеПростые примесиПрименение нескольких примесейПримеси против наследованияИтоги — Сборка робота