Menu
Coddy logo textTech

Переопределение методов

Часть раздела 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(), но каждый вид делает это по-своему.

challenge icon

Задание

Легко

Давайте построим небольшое царство животных, где разные существа говорят по-своему! Вы создадите родительский класс 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() для каждого, чтобы продемонстрировать, что одно и то же имя метода дает разный результат в зависимости от класса.

Вы получите два входных значения:

  1. Имя кошки
  2. Имя коровы

В вашем основном файле создайте обоих животных и вызовите :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() сначала для кошки, затем для коровы
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Object Oriented Programming