Menu
Coddy logo textTech

Общее и уникальное

Часть раздела Object Oriented Programming путешествия по Lua на Coddy — урок 35 из 70.

При работе с наследованием важно понимать, где находятся те или иные данные. Некоторые атрибуты являются общими — они определены в родительском классе и доступны всем дочерним классам. Другие являются уникальными — они создаются непосредственно в дочернем классе или экземпляре.

Рассмотрим следующую иерархию:

local Animal = {}
Animal.__index = Animal
Animal.kingdom = "Animalia"  -- Общее: определено в классе

function Animal:new(name)
    local obj = {name = name}  -- Уникальное: хранится в каждом экземпляре
    setmetatable(obj, Animal)
    return obj
end
local Dog = {}
Dog.__index = Dog
setmetatable(Dog, {__index = Animal})
Dog.sound = "bark"  -- Общий для всех собак

function Dog:new(name, breed)
    local obj = Animal.new(Animal, name)
    obj.breed = breed  -- Уникально: у каждой собаки своя порода
    setmetatable(obj, Dog)
    return obj
end

Когда вы создаете экземпляр Dog, некоторые значения существуют непосредственно в объекте, в то время как другие находятся через цепочку поиска:

local rex = Dog:new("Rex", "German Shepherd")

-- Уникально для этого экземпляра (хранится в rex)
print(rex.name)    -- "Rex"
print(rex.breed)   -- "German Shepherd"
-- Общие (найдены через поиск)
print(rex.sound)   -- "bark" (от Dog)
print(rex.kingdom) -- "Animalia" (от Animal)

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

challenge icon

Задание

Легко

Давайте изучим, как данные передаются через иерархию наследования, создав систему классов Creature и Dragon. Вы воочию увидите, какие атрибуты являются общими для всех экземпляров, а какие — уникальными для каждого отдельного объекта.

Вы организуете свой код в трех файлах:

  • Creature.lua: Определите родительский класс с общим атрибутом уровня класса world = "Fantasy Realm", который разделяют все существа. Включите конструктор :new(name), который сохраняет имя существа как атрибут экземпляра.
  • Dragon.lua: Определите дочерний класс, который наследуется от Creature. Добавьте общий атрибут уровня класса element = "fire", который разделяют все драконы. Ваш конструктор :new(name, color) должен вызывать родительский конструктор для имени, а затем добавлять color как уникальный атрибут экземпляра для каждого дракона.
  • main.lua: Создайте двух разных драконов и проверьте их атрибуты, чтобы продемонстрировать разницу между общими и уникальными данными.

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

  1. Имя и цвет первого дракона, разделенные запятой (например, Smaug,red)
  2. Имя и цвет второго дракона, разделенные запятой (например, Draco,gold)

В вашем основном файле создайте обоих драконов с их соответствующими именами и цветами. Затем выведите следующую информацию в строгом порядке:

  1. name первого дракона
  2. color первого дракона
  3. name второго дракона
  4. color второго дракона
  5. element первого дракона (общий из класса Dragon)
  6. element второго дракона (общий из класса Dragon)
  7. world первого дракона (общий из класса Creature)

Например, если входные данные — Smaug,red и Draco,gold, вывод должен быть следующим:

Smaug
red
Draco
gold
fire
fire
Fantasy Realm

Обратите внимание, что name и color у каждого дракона разные (уникальные атрибуты экземпляра), в то время как element и world одинаковы для обоих (общие атрибуты класса, найденные через цепочку поиска).

Шпаргалка

В иерархиях наследования атрибуты могут быть либо общими (определенными в таблице класса), либо уникальными (хранящимися в отдельных экземплярах).

Общие атрибуты определяются непосредственно в таблицах классов и доступны через цепочку поиска в метатаблице:

local Animal = {}
Animal.__index = Animal
Animal.kingdom = "Animalia"  -- Общий для всех Animals

Уникальные атрибуты присваиваются объекту экземпляра (obj) в конструкторах:

function Animal:new(name)
    local obj = {name = name}  -- Уникально для каждого экземпляра
    setmetatable(obj, Animal)
    return obj
end

Дочерние классы могут иметь свои собственные общие атрибуты, наследуя при этом атрибуты родителя:

local Dog = {}
Dog.__index = Dog
setmetatable(Dog, {__index = Animal})
Dog.sound = "bark"  -- Общий для всех Dogs

function Dog:new(name, breed)
    local obj = Animal.new(Animal, name)  -- Вызов родительского конструктора
    obj.breed = breed  -- Добавление уникального атрибута дочернего класса
    setmetatable(obj, Dog)
    return obj
end

Доступ к атрибутам:

local rex = Dog:new("Rex", "German Shepherd")

-- Уникальные (хранятся непосредственно в rex)
print(rex.name)    -- "Rex"
print(rex.breed)   -- "German Shepherd"

-- Общие (найдены через поиск в метатаблице)
print(rex.sound)   -- "bark" (из класса Dog)
print(rex.kingdom) -- "Animalia" (из класса Animal)

Попробуйте сами

-- main.lua: Создание драконов и демонстрация общих и уникальных атрибутов

local Dragon = require('Dragon')

-- Чтение ввода для первого дракона (имя,цвет)
local input1 = io.read()
-- Чтение ввода для второго дракона (имя,цвет)
local input2 = io.read()

-- TODO: Разобрать первый ввод, чтобы извлечь имя и цвет
-- Подсказка: Используйте string.match с шаблоном "([^,]+),([^,]+)"

-- TODO: Разобрать второй ввод, чтобы извлечь имя и цвет

-- TODO: Создать первого дракона с его именем и цветом

-- TODO: Создать второго дракона с его именем и цветом

-- TODO: Вывести следующее по порядку:
-- 1. Имя первого дракона
-- 2. Цвет первого дракона
-- 3. Имя второго дракона
-- 4. Цвет второго дракона
-- 5. Элемент первого дракона (общий из класса Dragon)
-- 6. Элемент второго дракона (общий из класса Dragon)
-- 7. Мир первого дракона (общий из класса Creature)
quiz iconПроверьте себя

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

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