Menu
Coddy logo textTech

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

Часть раздела Объектно-ориентированное программирование путешествия по 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)

Этот паттерн дает вам лучшее из обоих миров: четкую иерархию классов для общей идентичности, а также гибкие возможности, которые можно подмешивать там, где это необходимо.

challenge icon

Задание

Легко

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

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

  1. Имя птицы (строка)
  2. Умеет ли птица плавать: 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() только в том случае, если птица умеет плавать
quiz iconПроверьте себя

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

Все уроки раздела Объектно-ориентированное программирование