Простые примеси
Часть раздела Объектно-ориентированное программирование путешествия по Lua на Coddy. Урок 51 из 70.
Композиция позволяет помещать объекты внутрь других объектов. Но что, если ты хочешь использовать общее поведение в несвязанных классах?
И Player, и MenuItem может понадобиться метод :describe(), но у них нет общего родительского класса. Здесь на помощь приходят примеси.
Миксин — это просто таблица, содержащая функции. Вы «смешиваете» эти функции с классом, копируя их туда:
local Printable = {}
function Printable:describe()
print("Object: " .. tostring(self))
end
function Printable:log(message)
print("[LOG] " .. message)
endЧтобы применить эту примесь к классу, скопируй каждую функцию из таблицы примеси в таблицу класса:
local function applyMixin(class, mixin)
for key, value in pairs(mixin) do
class[key] = value
end
end
local Player = {}
Player.__index = Player
applyMixin(Player, Printable) -- Теперь у Player есть :describe() и :log()После применения примеси любой экземпляр Player может вызывать эти методы так, как если бы они были определены непосредственно в классе:
local p = setmetatable({}, Player)
p:log("Player created") -- [LOG] Player созданВ отличие от наследования, примеси не создают отношения родителя и потомка. Вы просто копируете функции из одной таблицы в другую.
Благодаря этому миксины идеально подходят для совместного использования вспомогательных возможностей, таких как ведение журнала, сериализация или методы описания, в классах, у которых в остальном нет ничего общего.
Задание
ЛегкоДавайте создадим систему тегов с помощью миксинов! Вы создадите миксин Taggable, который можно применять к любому классу, чтобы объекты могли управлять коллекцией тегов. Это полезно для категоризации элементов, публикаций или любых сущностей, которым нужны метки.
Вы распределите код по трём файлам:
Taggable.lua: создайте таблицу миксина с двумя методами::addTag(tag): добавляет тег вself.tags(предполагается, что таблица существует):listTags(): выводит все теги в форматеTags: tag1, tag2, tag3(илиTags: none, если тегов нет)
Article.lua: создайте класс Article с конструктором:new(title), который сохраняет заголовок и инициализирует пустую таблицуtags. Добавьте метод:getTitle(), который возвращает заголовок статьи.main.lua: напишите здесь вспомогательную функциюapplyMixin, а затем примените миксин с помощьюapplyMixin(Article, Taggable), чтобы экземпляры Article могли работать с тегами. Затем считайте заголовок статьи и два тега. Создайте Article, добавьте к нему оба тега, выведите заголовок и перечислите все теги.
На вход будут поданы три значения:
- Заголовок статьи (строка)
- Первый тег для добавления (строка)
- Второй тег для добавления (строка)
В выводе должны быть заголовок статьи, а затем её теги:
Title: {title}
Tags: {tag1}, {tag2}Например, если на вход поданы Lua Basics, programming и tutorial, вывод должен быть таким:
Title: Lua Basics
Tags: programming, tutorialПомните: методы миксина работают, потому что у экземпляров Article есть таблица tags, с которой могут работать методы миксина. Миксин не создаёт эту таблицу. Это делает конструктор Article. Миксин лишь предоставляет общее поведение!
Попробуйте сами
-- Основной файл системы тегов
local Taggable = require('Taggable')
local Article = require('Article')
-- TODO: Реализовать функцию applyMixin
-- Эта функция должна копировать все методы из таблицы mixin в таблицу target
function applyMixin(target, mixin)
-- TODO: Перебрать таблицу mixin и скопировать каждый метод в target
end
-- TODO: Применить mixin Taggable к Article с помощью applyMixin
-- Считать входные данные
local title = io.read()
local tag1 = io.read()
local tag2 = io.read()
-- TODO: Создать Article с заданным заголовком
-- TODO: Добавить оба тега к статье
-- TODO: Вывести заголовок в формате: Title: {title}
-- TODO: Вывести все теги с помощью метода listTags
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Концепция 'self'
Таблицы с функциямиЯвный 'self'Синтаксис с двоеточиемТочка против двоеточияИтоги — Движущаяся точка4Проект: Цифровой банк
Настройка проектаМетод пополнения7Полиморфизм и переопределение
Переопределение методовВызов родительских методовУтиная типизацияОбщий интерфейсПроверка типаИтоги — Роли сотрудников2Паттерн прототипа класса
Концепция прототипаСвязывание через __indexКонструктор :new()Инициализация атрибутовНезависимые экземплярыИтоги — Автозавод5Перегрузка операторов в ООП
Сложение объектовВычитание объектовКонкатенация объектовСравнение объектов (<, >)Повторение — Математика кошелька8Инкапсуляция
Соглашения об именованииЗамыкания для приватностиДоступ через замыканияТаблицы только для чтенияЛогика валидацииИтоги — Защищенное хранилище11Паттерны проектирования (Lite)
Фабричные функцииТаблица SingletonПаттерн IteratorObserver (Слушатель)Итоги — Logger Factory3Состояние и поведение объектов
Переменные экземпляраГеттерыСеттерыВычисляемые свойстваФорматирование строкПроверка на равенствоПовторение — Оценка студента6Основы наследования
Настройка наследованияНаследование методовРасширение конструктораДобавление дочерних методовОбщее и уникальноеИтоги: Иерархия фигур9Композиция и примеси
Отношение "Has-A"ДелегированиеПростые примесиПрименение нескольких примесейПримеси против наследованияИтоги — Сборка роботаПотренируйтесь самостоятельно: Онлайн-компилятор Lua