Примеси против наследования
Часть раздела Объектно-ориентированное программирование путешествия по Lua на Coddy — урок 53 из 70.
Вы изучили два способа совместного использования поведения: наследование создает отношение «является» (is-a) через цепочку прототипов, в то время как примеси копируют функции непосредственно в класс. Но когда следует использовать каждый из этих подходов?
Используйте наследование, когда объекты имеют общую фундаментальную идентичность. Bird является Animal — для Bird имеет смысл наследовать основные формы поведения животных, такие как :eat() или :sleep(). Эта связь является постоянной и определяет, чем объект является по своей сути.
Используйте примеси, когда вы хотите добавить возможность, которая не определяет основную идентичность объекта.
Не все птицы умеют летать — пингвины и страусы являются птицами, но они не летают. Полет — это способность, а не определяющая черта птицы. Примесь Flyable позволяет добавить возможность полета тем птицам, которым она нужна, не навязывая ее всем остальным.
Эти два подхода прекрасно работают вместе:
-- Bird наследует от Animal (отношение is-a)
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})
-- Flyable — это способность (mixin)
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
-- Применяем примесь (mixin) только к птицам, которые умеют летать
applyMixin(Bird, Flyable)Этот паттерн дает вам лучшее из обоих миров: четкую иерархию классов для общей идентичности, а также гибкие возможности, которые можно подмешивать там, где это необходимо.
Задание
ЛегкоДавайте создадим небольшую экосистему существ, которая продемонстрирует, когда использовать наследование, а когда — примеси (mixins)! Вы создадите базовый класс Animal, класс Bird, который наследуется от него, и примесь Swimmable — потому что, хотя все птицы являются животными, не все птицы умеют плавать.
Вы распределите свой код по четырем файлам:
Animal.lua: Создайте базовый класс Animal с конструктором:new(name), который сохраняет имя. Добавьте метод:eat(), который выводит{name} is eating. Это представляет собой основную идентичность, общую для всех животных.Swimmable.lua: Создайте примесь с методом:swim(), который выводит{self.name} is swimming. Это способность — не все существа умеют плавать, поэтому она не должна быть частью цепочки наследования.Bird.lua: Создайте класс Bird, который наследуется от Animal. Конструктор:new(name, canSwim)должен вызывать родительский конструктор и сохранять информацию о том, умеет ли эта птица плавать. Примените примесь Swimmable к классу Bird. Добавьте метод:chirp(), который выводит{name} chirps!— это поведение, специфичное для птиц.main.lua: Напишите здесь вспомогательную функциюapplyMixin. Считайте входные данные для имени птицы и того, умеет ли она плавать (yesилиno). Создайте экземпляр Bird, затем продемонстрируйте его способности: вызовите:eat()(унаследовано от Animal), вызовите:chirp()(собственный метод Bird) и вызывайте:swim()только в том случае, если птица умеет плавать.
Вы получите два входных значения:
- Имя птицы (строка)
- Умеет ли птица плавать:
yesилиno
Если птица умеет плавать, ваш вывод должен быть следующим:
{name} is eating
{name} chirps!
{name} is swimmingЕсли птица не умеет плавать, ваш вывод должен быть следующим:
{name} is eating
{name} chirps!Например, если входные данные — Duck и yes, вывод должен быть:
Duck is eating
Duck chirps!
Duck is swimmingЕсли входные данные — Sparrow и no, вывод должен быть:
Sparrow is eating
Sparrow chirps!Эта задача показывает ключевое различие: Bird является Animal (наследование), но плавание — это необязательная способность (примесь). Примесь применяется к классу, но ваша основная логика решает, использовать ли её, основываясь на способностях конкретной птицы!
Попробуйте сами
-- main.lua: Точка входа - создание птицы и демонстрация её способностей
-- Вспомогательная функция для применения примеси (mixin) к классу
local function applyMixin(class, mixin)
-- TODO: Скопировать все методы из примеси в класс
end
-- Применить примесь Swimmable к Bird перед подключением Bird
-- Примечание: Возможно, это нужно будет обработать в Bird.lua или здесь
local Bird = require('Bird')
local Swimmable = require('Swimmable')
-- Применить примесь к классу Bird
applyMixin(Bird, Swimmable)
-- Чтение входных данных
local name = io.read()
local canSwimInput = io.read()
-- TODO: Определить, может ли птица плавать, на основе ввода ("yes" или "no")
-- TODO: Создать экземпляр Bird с именем и способностью к плаванию
-- TODO: Вызвать eat() - унаследовано от Animal
-- TODO: Вызвать chirp() - собственный метод Bird
-- TODO: Вызывать swim() только в том случае, если птица умеет плавать
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Концепция 'self'
Таблицы с функциямиЯвный 'self'Синтаксис с двоеточиемТочка против двоеточияИтоги — Движущаяся точка4Проект: Цифровой банк
Настройка проектаМетод пополнения7Полиморфизм и переопределение
Переопределение методовВызов родительских методовУтиная типизацияОбщий интерфейсПроверка типаИтоги — Роли сотрудников2Паттерн прототипа класса
Концепция прототипаСвязывание через __indexКонструктор :new()Инициализация атрибутовНезависимые экземплярыИтоги — Автозавод5Перегрузка операторов в ООП
Сложение объектовВычитание объектовКонкатенация объектовСравнение объектов (<, >)Повторение — Математика кошелька8Инкапсуляция
Соглашения об именованииЗамыкания для приватностиДоступ через замыканияТаблицы только для чтенияЛогика валидацииИтоги — Защищенное хранилище11Паттерны проектирования (Lite)
Фабричные функцииТаблица SingletonПаттерн IteratorObserver (Слушатель)Итоги — Logger Factory3Состояние и поведение объектов
Переменные экземпляраГеттерыСеттерыВычисляемые свойстваФорматирование строкПроверка на равенствоПовторение — Оценка студента6Основы наследования
Настройка наследованияНаследование методовРасширение конструктораДобавление дочерних методовОбщее и уникальноеИтоги: Иерархия фигур9Композиция и примеси
Отношение "Has-A"ДелегированиеПростые примесиПрименение нескольких примесейПримеси против наследованияИтоги — Сборка робота