Примеси против наследования
Часть раздела Объектно-ориентированное программирование путешествия по Lua на Coddy. Урок 53 из 70.
Ты узнал два способа совместного использования поведения: наследование создаёт отношение «является разновидностью» через цепочку прототипов, а примеси напрямую копируют функции в класс. Но когда следует использовать каждый из этих подходов?
Используй наследование, когда объекты имеют общую сущностную природу. Bird — это Animal. Логично, чтобы Bird наследовал основные действия животных, такие как :eat() или :sleep(). Это постоянная связь, определяющая сущность объекта.
Используй примеси, когда хочешь добавить возможность, которая не определяет основную сущность объекта.
Не все птицы умеют летать: пингвины и страусы — птицы, но они не летают. Полёт — это способность, а не определяющий признак птицы. Миксин Flyable позволяет добавить способность летать тем птицам, которым это нужно, не навязывая её всем.
Эти два подхода работают вместе. Здесь и Eagle, и Penguin наследуют от Bird, а примесь получает только Eagle:
-- Bird наследуется от Animal (отношение «является»)
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})
-- Eagle и Penguin — птицы
local Eagle = setmetatable({}, {__index = Bird})
Eagle.__index = Eagle
local Penguin = setmetatable({}, {__index = Bird})
Penguin.__index = Penguin
-- Flyable — это возможность (примесь)
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
-- Примените примесь только к птицам, которые летают
applyMixin(Eagle, Flyable)Орёл может вызвать :fly(). Пингвин не может: fly нет ни в Penguin, ни в Bird, ни в Animal, поэтому penguin:fly() вызывает ошибку. Если применить примесь к Bird, то fly скопируется в каждую птицу, включая пингвинов.
Задание
ЛегкоДавайте создадим небольшую экосистему существ, которая покажет, когда использовать наследование, а когда — примеси! Вы создадите базовый класс 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)должен вызывать конструктор родительского класса. Добавьте метод:chirp(), который выводит{name} chirps!. Это поведение, характерное для птиц. Не добавляйте плавание в Bird: не каждая птица умеет плавать.main.lua: Напишите здесь вспомогательную функциюapplyMixin(target, mixin): она копирует все функции примеси в целевой объект. Считайте имя птицы и информацию о том, умеет ли она плавать (yesилиno). Создайте объект Bird и, если он умеет плавать, примените примесь Swimmable только к этой птице. Затем вызовите:eat()(унаследованный от Animal) и:chirp()(собственный метод Bird), а:swim()вызывайте, только если у этой птицы есть такой метод (if bird.swim then).
Вы получите два входных значения:
- Имя птицы (строка)
- Умеет ли птица плавать:
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 (наследование), а плавание — необязательная способность (примесь). Поскольку примесь добавляется только птицам, умеющим плавать, у птицы, которая не умеет плавать, вообще нет метода swim.
Попробуйте сами
-- main.lua: точка входа — создать птицу и продемонстрировать её способности
-- Вспомогательная функция для применения миксина к классу или отдельному объекту
local function applyMixin(target, mixin)
-- TODO: Скопировать все функции из mixin в target
end
local Bird = require('Bird')
local Swimmable = require('Swimmable')
-- Считать входные данные
local name = io.read()
local canSwimInput = io.read()
-- TODO: Создать экземпляр Bird с заданным именем
-- TODO: Если входное значение — "yes", применить миксин Swimmable только к этой птице
-- 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"ДелегированиеПростые примесиПрименение нескольких примесейПримеси против наследованияИтоги — Сборка роботаПотренируйтесь самостоятельно: Онлайн-компилятор Lua