Переопределение методов
Часть раздела Object Oriented Programming путешествия по Lua на Coddy — урок 37 из 70.
До сих пор дочерние классы наследовали методы родительских классов и добавляли новые. Но что, если дочернему классу нужно то же самое имя метода с другим поведением? Это называется переопределением метода (method overriding).
Когда вы определяете метод в дочернем классе с тем же именем, что и в родительском, версия дочернего класса имеет приоритет. Цепочка поиска Lua сначала проверяет дочерний класс — если она находит метод там, поиск прекращается.
local Animal = {}
Animal.__index = Animal
function Animal:new(name)
local obj = {name = name}
setmetatable(obj, Animal)
return obj
end
function Animal:speak()
print(self.name .. " makes a sound")
end
-- Dog переопределяет speak()
local Dog = {}
Dog.__index = Dog
setmetatable(Dog, {__index = Animal})
function Dog:new(name)
local obj = Animal.new(Animal, name)
setmetatable(obj, Dog)
return obj
end
function Dog:speak()
print(self.name .. " barks: Woof!")
endТеперь каждый класс по-разному реагирует на один и тот же вызов метода:
local generic = Animal:new("Creature")
local buddy = Dog:new("Buddy")
generic:speak() -- Вывод: Creature makes a sound
buddy:speak() -- Вывод: Buddy barks: Woof!Метод :speak() класса Dog полностью заменяет родительскую версию для экземпляров Dog. Это позволяет создавать специализированное поведение, сохраняя при этом единообразный интерфейс — каждое животное может вызвать :speak(), но каждый вид делает это по-своему.
Задание
ЛегкоДавайте построим небольшое царство животных, где разные существа говорят по-своему! Вы создадите родительский класс Animal и два дочерних класса — Cat и Cow — каждый из которых переопределяет родительский метод :speak() своим собственным специализированным поведением.
Вы распределите свой код по четырем файлам:
Animal.lua: Ваш базовый класс с конструктором:new(name), который сохраняет имя животного. Включите метод:speak(), который выводит имя животного, за которым следуетmakes a sound. Это поведение по умолчанию, которое дочерние классы будут переопределять.Cat.lua: Дочерний класс, наследуемый от Animal. Его конструктор:new(name)должен вызывать родительский конструктор, а затем повторно привязывать метатаблицу к Cat. Переопределите метод:speak()так, чтобы кошки говорили что-то другое — выводите имя кошки, за которым следуетmeows: Meow!Cow.lua: Еще один дочерний класс, наследуемый от Animal. Настройте конструктор так же, как и в Cat. Переопределите:speak()так, чтобы коровы выводили свое имя, за которым следуетmoos: Moo!main.lua: Соберите все вместе! Прочитайте два имени из входных данных, создайте Cat с первым именем и Cow со вторым именем, затем вызовите:speak()для каждого, чтобы продемонстрировать, что одно и то же имя метода дает разный результат в зависимости от класса.
Вы получите два входных значения:
- Имя кошки
- Имя коровы
В вашем основном файле создайте обоих животных и вызовите :speak() сначала для кошки, а затем для коровы.
Например, если входные данные — Whiskers и Bessie, вывод должен быть следующим:
Whiskers meows: Meow!
Bessie moos: Moo!Обратите внимание, как оба животных реагируют на один и тот же вызов метода :speak(), но каждое выдает результат, специфичный для его типа. Версия Cat полностью заменяет общее поведение родителя "makes a sound", и то же самое делает версия Cow. Это и есть переопределение методов в действии — один интерфейс, разные реализации!
Шпаргалка
Переопределение методов позволяет дочернему классу определять метод с тем же именем, что и метод родительского класса, заменяя поведение родителя специализированной функциональностью.
Когда Lua ищет метод, она сначала проверяет дочерний класс. Если она находит метод там, она использует эту версию и прекращает поиск в родительском классе.
Базовый пример переопределения метода:
local Animal = {}
Animal.__index = Animal
function Animal:new(name)
local obj = {name = name}
setmetatable(obj, Animal)
return obj
end
function Animal:speak()
print(self.name .. " makes a sound")
end
-- Dog overrides speak()
local Dog = {}
Dog.__index = Dog
setmetatable(Dog, {__index = Animal})
function Dog:new(name)
local obj = Animal.new(Animal, name)
setmetatable(obj, Dog)
return obj
end
function Dog:speak()
print(self.name .. " barks: Woof!")
endИспользование переопределенного метода:
local generic = Animal:new("Creature")
local buddy = Dog:new("Buddy")
generic:speak() -- Output: Creature makes a sound
buddy:speak() -- Output: Buddy barks: Woof!Метод дочернего класса полностью заменяет версию родителя для экземпляров этого дочернего класса, обеспечивая специализированное поведение при сохранении согласованного интерфейса во всех классах.
Попробуйте сами
-- main.lua: Собираем всё вместе
local Cat = require('Cat')
local Cow = require('Cow')
-- Чтение ввода
local catName = io.read()
local cowName = io.read()
-- TODO: Создать объект Cat с первым именем
-- TODO: Создать объект Cow со вторым именем
-- TODO: Вызвать :speak() сначала для кошки, затем для коровы
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Концепция 'self'
Таблицы с функциямиЯвный 'self'Синтаксис с двоеточиемТочка против двоеточияИтоги — Движущаяся точка4Проект: Цифровой банк
Настройка проектаМетод пополнения7Полиморфизм и переопределение
Переопределение методовВызов родительских методовУтиная типизацияОбщий интерфейсПроверка типаИтоги — Роли сотрудников2Паттерн прототипа класса
Концепция прототипаСвязывание через __indexКонструктор :new()Инициализация атрибутовНезависимые экземплярыИтоги — Автозавод5Перегрузка операторов в ООП
Сложение объектовВычитание объектовКонкатенация объектовСравнение объектов (<, >)Повторение — Математика кошелька8Инкапсуляция
Соглашения об именованииЗамыкания для приватностиДоступ через замыканияТаблицы только для чтенияЛогика валидацииИтоги — Защищенное хранилище11Паттерны проектирования (Lite)
Фабричные функцииТаблица SingletonПаттерн IteratorObserver (Слушатель)Итоги — Logger Factory3Состояние и поведение объектов
Переменные экземпляраГеттерыСеттерыВычисляемые свойстваФорматирование строкПроверка на равенствоПовторение — Оценка студента6Основы наследования
Настройка наследованияНаследование методовРасширение конструктораДобавление дочерних методовОбщее и уникальноеИтоги: Иерархия фигур