Menu
Coddy logo textTech

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

Часть раздела Объектно-ориентированное программирование путешествия по Lua на Coddy. Урок 53 из 70.

Вы изучили два способа совместного использования поведения: наследование создаёт отношение «является» через цепочку прототипов, а примеси напрямую копируют функции в класс. Но когда следует использовать каждый из этих подходов?

Используй наследование, когда объекты имеют общую фундаментальную сущность. Bird является Animal. Имеет смысл, чтобы Bird наследовал основные поведенческие характеристики животных, такие как :eat() или :sleep(). Это постоянная связь, определяющая, чем объект является по своей сути.

Используйте примеси, если хотите добавить возможность, которая не определяет основную сущность объекта.

Не все птицы умеют летать: пингвины и страусы — птицы, но они не летают. Полёт — это capability, а не определяющий признак птицы. Миксин Flyable позволяет добавить способность к полёту птицам, которым она нужна, не навязывая её всем.

Два подхода прекрасно работают вместе:

-- Bird наследует от Animal (отношение is-a)
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})

-- Flyable — это способность (миксин)
local Flyable = {}
function Flyable:fly()
    print(self.name .. " soars through the sky!")
end

-- Применить миксин только к птицам, которые могут летать
applyMixin(Bird, Flyable)

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

challenge icon

Задание

Легко

Давайте создадим небольшую экосистему существ, которая демонстрирует, когда использовать наследование, а когда — примеси! Вы создадите базовый класс 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: Точка входа - создать птицу и продемонстрировать её способности

-- Вспомогательная функция для применения миксина к классу
local function applyMixin(class, mixin)
    -- TODO: Скопировать все методы из mixin в class
    
end

-- Применить миксин Swimmable к Bird перед require Bird
-- Note: Возможно, вам нужно обработать это в Bird.lua или здесь

local Bird = require('Bird')
local Swimmable = require('Swimmable')

-- Применить mixin к классу 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Проверьте себя

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

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

Потренируйтесь самостоятельно: Онлайн-компилятор Lua