Zer0Galaxy 2 187 Опубликовано: 11 июня, 2014 В данной статье я хотел бы показать, как можно применять объектно-ориентированный подход при программировании на lua. Если Вы ранее не сталкивались с ООП в других языках, статья вряд ли станет Вам полезной ибо не предназначена для обучения объектно-ориентированному мышлению, а лишь приводит пример реализации объектов. Lua не относится к объектно-ориентированным языкам, поскольку не содержит стандартных механизмов создания и использования объектов. Но lua-таблица является настолько гибким инструментом, что позволяет реализовать практически любую структуру, присущую другим языкам. В том числе и объекты. Прежде чем создавать экземпляры объекта, необходимо описать соответствующий ему класс. ClassA={} ClassA.__index=ClassAПо большому счету, называть это классом нельзя, поскольку класс — это абстракция, служащая шаблоном для создания объекта. Сдесь же мы имеем дело с вполне реальной таблицей. Поэтому ClassA будем называть объектом-прототипом.Как видите, я не описал никаких полей объекта-прототипа т. к. в отличии от таких языков как Delphi и C++, поля таблицы в lua можно описывать когда угодно, а не только при создании таблицы. Поля будем прописывать в конструкторе класса при создании экземпляра объекта. Единственное поле __index содержит указатель на сам класс. Это нужно будет для правильной работы оператора self. Создадим конструктор: function ClassA:new(_Name) local obj={Name = _Name} setmetatable(obj,self) print('Constructor ClassA for '..obj.Name) return obj endЧто делает конструктор?Строка 1. Создает экземпляр теперь уже объекта, описывает поля объекта и присваивает полям начальные значения. Строка 2. Переопределяет метатаблицу вновь созданного объекта, в результате чего объект получает доступ к методам класса. Методов пока нет, но они будут описаны позже. Строка 3. Это необязательная строка. Я ее сюда вставил для демонстрации работы конструктора. Строка 4. Возвращает созданный объект. Теперь создадим парочку методов: function ClassA:Metod1() print('Metod1 of ClassA for '..self.Name) end function ClassA:Metod2() print('Metod2 of ClassA for '..self.Name) endЗачем два? В дальнейшем один из них будет наследован, а другой перекрыт методом наследника. И так, мы создали объект-прототип. Пора создавать экземпляры объекта. Экземпляры будем хранить в таблице objects. Для создания экземпляра вызываем конструктор objects={} for i = 1,2 do objects[i] = ClassA:new('Object#'..i) end Смотрим что получилось for i=1,2 do objects[i]:Metod1() objects[i]:Metod2() end Вот программа в полном сборе. ClassA={} ClassA.__index=ClassA function ClassA:new(_Name) local obj={Name = _Name} setmetatable(obj,self) print('Constructor ClassA for '..obj.Name) return obj end function ClassA:Metod1() print('Metod1 of ClassA for '..self.Name) end function ClassA:Metod2() print('Metod2 of ClassA for '..self.Name) end objects={} for i = 1,2 do objects[i] = ClassA:new('Object#'..i) end for i=1,2 do objects[i]:Metod1() objects[i]:Metod2() end Если все сделано правильно, после запуска программы Вы должны наблюдать вот такие сообщения: [ATTACH]115[/ATTACH] В следующий раз я расскажу о наследовании методов объекта и их полиморфизме. 2 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Zer0Galaxy Автор темы 2 187 Опубликовано: 12 июня, 2014 В прошлый раз мы научились создавать объекты в lua и вызывать их методы. Нами был создан класс ClassA (хорошо, не класс, а объект-прототип), имеющий одно поле (Name) и два метода (Metod1, Metod2). Были созданы два экземпляра объектов и вызваны их методы. Сегодня мы попытаемся создать наследника от ClassA, добавим ему еще одно поле, унаследуем один из методов без изменений, а второй переопределим. Так же как и класс-родитель, наследник изначально представляет собой пустую таблицу: ClassB={} ClassB.__index=ClassB setmetatable(ClassB,ClassA)После выполнения setmetatable, ClassB внешне будет представлять собой точную копию ClassA, т. е. унаследует все его методы.Изменения начнем с конструктора function ClassB:new(_Name,_Size) local obj=ClassA:new(_Name) obj.Size=_Size setmetatable(obj,self) print('Constructor ClassB for '..obj.Name) return obj endКаковы отличия в конструкторах родителя и наследника? Как видим, для создания объекта наследник использует конструктор родителя, а не создает объект «с нуля». Это конечно не обязательно, но такова классика. Другое отличие — новое поле, которое будет хранить воображаемый размер нашего объекта.Переходим к методам. Metod1, как и договаривались оставляем нетронутым, а вот Metod2 переопределим. function ClassB:Metod2() ClassA.Metod2(self) --inherited print('Metod2 of ClassB for '..self.Name..'. Size is '..self.Size) endТут я показал как из метода объекта-наследника вызвать метод объекта-родителя. Но это тоже при необходимости.Вот собственно и всё. Помимо ранее созданных объектов ClassA, создаем еще два экземпляра нового объекта objects={} for i = 1,2 do objects[i] = ClassA:new('Object#'..i) end for i = 3,4 do objects[i] = ClassB:new('Object#'..i, i*i) endИ посмотрим что получилось for i=1,4 do objects[i]:Metod1() objects[i]:Metod2() endУ меня получилось вот что:[ATTACH]116[/ATTACH] Обращаю внимание, что для объектов 3 и 4 конструктор и Metod2 выполняются дважды — сначала от ClassA, затем от ClassB. Мы рассмотрели такие особенности ООП как наследование и полиморфизм. А вот как реализовать на lua сокрытие данных объекта от доступа извне (по моему, это называется инкапсуляцией) я пока не знаю. Если знаете — пишите. Для тех кто все таки решил попробовать себя в объектно-ориентированном программировании, напоминаю — следите за пунктуацией. Имя поля отделяем точкой, имя метода — двоеточием. Это важно. 4 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Alex 4 683 Опубликовано: 3 июня, 2015 Нашел в Блоге еще записи о программировании в стиле ООП на луа, но там как-то все сумбурно. Дмитрий, Вячеслав. Просьба! Напишите понятным языком, как оно должно выглядеть на примере. Я там попросил в блоге Вячеслава, чтобы вы нам, нубасам, объяснили про метаметоды и прочее, приват и паблик свойства, как избавиться от двоеточия без явного указания аргумента self или чего там. Вот мол, молоток, вот гвоздь, забивается он так-то и так-то. Не знаю, правда, нужна ли эта абракадабра для наших черепашек , я лично обхожусь программками маленькими, написанными в процедурном стиле, мне этого хватает с головой, но все же. Видимо для больших проектов, сложных графических интерфейсов и прочего можно применить парадигму ООП. Если там грамотный и мощный класс сконструирован с кучей полезных методов, почему и не использовать их при создании уже своих классов с наследованием. Может, народ будет создавать объекты с описанием свойств реакторов, и писать им методы отгрузки топлива и управления мощностью. Не сильно даже пока понимаю, где оно нужно. вот, что пока понятно простому обычному человеку и мне в частности, если что не так, поправьте, что я тут написал: term = require('term') term.clear() --Создадим два класса, родительский и класс потомок -- ==========Cоздадим класс Игроки, это родительский класс=============== -- примечание: Я создал класс Игроки, но это не абстрактный класс, так как он позволяет создавать непосредственно -- экземляр класса, то есть конкрентый объект. Абтрактные классы можно только наследовать и они должны -- иметь хотя бы один абстрактный метод, но и производным классом его назвать нельзя, видимо, так как он ни от -- какого класса не наследует ни свойств, ни методов. print('============ ВЫПОЛНЕНИЯ ПРОГРАММКИ ===========') ClassPlayers = {} -- теперь проинициализируем поля, процедуры и фунции нашего класса function ClassPlayers:new(age, uuid, ip) local obj = {} obj.age = age or nil -- возраст, по умолчанию nil obj.uuid = uuid or 'uuid' -- по умолчанию 'uuid' obj.ip = ip or '1.1.1.1' -- по умолчанию '1.1.1.1' --далее превращаем таблицу в класс setmetatable(obj,self) self.__index = self return obj -- возвращаем наш объект (экземпляр класса) end -- функция получения свойств function ClassPlayers:get() return self.age, self.uuid, self.ip end -- функция изменения свойств function ClassPlayers:set(age, uuid, ip) self.age = age self.uuid = uuid self.ip = ip end -- создадим наших первых игроков! Это конкретные объекты, экземпляры класса Игроки Alex = ClassPlayers:new(36, "abc-zxc-1234567-vvv", '192.168.1.0') -- мы создали объект Алекс, у которого есть какие-то свойства Bob = ClassPlayers:new(28, "zxc-qwe-0987654-bbb", '192.168.1.2') -- мы создали объект Боб, у которого есть какие-то свойства print('>>> проверим объект Алекс') print(Alex:get()) print('>>> меняем Алекса и проверяем, что получилось') Alex:set( 35, '111-aaa-bbb', '192.168.1.1') print(Alex:get()) -- =========Создадим подкласс Администраторы========== ClassAdmins = {} --пусть у этого подкласса будет свой метод Бан и метод Бла-бла, а так же один переназначенный метод гет function ClassAdmins:blabla() print('Отлично, метот Бла-бла у класса \'Админы\' выполнился') -- здесь может быть все, что угодно, по идее end --Админы имеют также медод Бан function ClassAdmins:ban(user) print ('Юзер '..user..' забанен навеки вечные!!!') end --переназначение метода гет у подкласса Админы function ClassAdmins:get() return "Это злые админы, всех банят и удаляют моды :)" end --наследуемся от родительского класса Игроки setmetatable(ClassAdmins,{__index = ClassPlayers}) -- Создаем дефолтного пока игрока ГрингоСтар без свойств, здесь видно, что метод new используется у родительского класса Игроки. Он там уже один раз -- описан, и его не нужно снова писать в коде и классе для админов. Это, cудя по всему, ключевой момент ООП. print('>>> Создаем и проверяем нашего Гринго, пока это какая-то какашка по умолчанию') -- но это уже самостоятельный объект GringoStar = ClassAdmins:new() --создали админа Гринго со стандарными свойствами ИГРОКА, мы не задали пока ему свойств, хоть и можно было!!! print(ClassPlayers.get(GringoStar)) print('>>> Меняем нашего Гринго так как ему положено быть') GringoStar:set(23, "555-666-sssssss-uuu", '192.168.255.255') -- изменили Гринге свойства, метод set ТОЖЕ описан уже в классе ИГРОКИ!!! print('>>> Вызовем метод гет у Админов') print(ClassAdmins:get()) print('>>> Вызовем метод гет у Игроков для нашего Гринго') -- синтаксис: РодительскийКласс.Метод(сам_объект, параметры) print(ClassPlayers.get(GringoStar)) print('>>> Вызовем метод Бла-бла у Админов') ClassAdmins:blabla() print('>>> Вызовем метод Бан у Админов и в качестве аргумента\nпередадим на растерзание дюпера Васю из 3-А') ClassAdmins:ban('Вася') print('>>> обращаюсь к некоторому свойству Алекса') print(Alex.age) print('>>> Меняю его на 50, Alex.age = 50') Alex.age = 50 print('>>> смотрю свойство age Алекса : ',Alex.age) print('>>> смотрю свойство uuid Гринго : ',GringoStar.uuid) ---Что получилось? Здесь, по идее и замыслу ООП, Гринго обладает всеми свойствами и методами класса Игроки, то есть наследует их, ---а также имеет свои свойтсва и методы класса Админы. вот, что выдала программка: Просьба, можно ли так же с объяснениями написать, если не затруднит о том, что я просил выше, на конкретном примерчике? 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
NEO 541 Опубликовано: 3 июня, 2015 Лучше изучить ооп на Java, а потом применять знания эти в луа, так как тут псевдо ооп, изучать ооп в луа испортит все представление об ооп. 2 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Alex 4 683 Опубликовано: 3 июня, 2015 Лучше изучить ооп на Java, а потом применять знания эти в луа, так как тут псевдо ооп, изучать ооп в луа испортит все представление об ооп. Это понятно. Изучать что-то можно всю жизнь, век живи, век учись, все равно нубом помрешь, как сказал Абдулла ибн Абдурахман. И это если ты по специальности программист. А поиграться в майн ради хобби, это совсем другое. Настоящие программисты нормальные кусок хлеба себе зарабатывают с маслом и икрой черной, а не в какахомайн играют и багованных черепах даже не пытаются запрогать, а мы так, кубики в цикле гоням, считай Нео, это же игра просто. Ты через чур ее близко к сердцу сильно прижал. За сервера переживаешь, браузеры на ОС и пинг стабильный чтобы был у твоей ОпенНет. Все равно что шутер-геймер спросил, какие перки на прокачку плазмогана есть и какие лучше пульки юзать, а ты ему говоришь. Э-э-э-э-э-э-э, чува-а-а-ак, какие пульки, лучше в школу "Морских котиков" в Детройте записывайся и станешь мега-крутым снайпером, дадут пукалку настоящую Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Krutoy 1 169 Опубликовано: 3 июня, 2015 Вячеслав. Просьба! Напишите понятным языком, как оно должно выглядеть на примере. Я ООП сам не открывал а искал в гугле. Я могу сделать статью из копипасты с гугла. Сойдет? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Alex 4 683 Опубликовано: 4 июня, 2015 Я ООП сам не открывал а искал в гугле. Я могу сделать статью из копипасты с гугла. Сойдет? Конечно сойдет! В нете то любой полазить может и инфы там много всякой и винегрета. Но важен пример конкретный. Вот, мол, готовый класс с наследованием для программки для робота, печки, реактора и прочего(с комментариями в коде) Методы не нужны подробные. Важна структура только. Что за подчеркивания одинарные и двойные, что они значат, можно ли скрыть(запретить) изменение свойств вне класса, метаметоды, как уйти от двоеточий, я не сильно понял в той статье, что там за абракадабра. Ты же сам говоришь, что можно так сделать как-то и т.п. У меня солдаты, некоторые не умели читать и писать толком, управляли многомиллионной по стоимости военной техникой высокоточной сложнейшей радиоэлектронной, подстраивали ее и выполняли боевые задачи повышенной важности и сложности по отработке нормативов и выдаче радиолокационной информации о воздушных целях и несли боевое дежурство. Но не имели понятия о транзисторах и законе Ома даже. А знаешь почему? Потому что практически я им показал и рассказал, как и что делать, просто на пальцах объяснил и натренировал их до автоматизма, как обезьян. Понимаешь меня? Думаю, что для ОпенГлассес вполне себе можно написать в стиле ООП несколько мощных классов и пусть народ их юзает на проекте нашем в свое удовольствие, так ведь? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
MisterFunny01 4 Опубликовано: 2 апреля, 2019 03.06.2015 в 15:38, NEO сказал: Лучше изучить ооп на Java Или на с++ (кст я его и изучаю) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Clor 106 Опубликовано: 2 мая, 2022 (изменено) Вот такой класс получился нормально? https://github.com/Clor0xD/OpenComputers2IDE.git посмотреть lua ооп потуги можно тут) https://github.com/Tencent/LuaHelper отличный плагин на vscode классы (включая множественное наследование), типы, подсказки, аннотации ---@class IRunable local IRunable IRunable = {class = "Interface IRunable"} function IRunable:run(...) self:noImplIntefaceError("run(...)", IRunable:getClassName()) end return IRunable --- --- Generated by EmmyLua(https://github.com/EmmyLua) --- Created by User. --- DateTime: 20.04.2022 20:14 --- local Class = require('libEx/Class') local sides = require("sides") ---@class Position:Class ---@field public x number ---@field public y number ---@field public z number ---@field public r PositionSide local Position = Class:extended({ class = "Class Position" }) ---@class PositionSide Position.side = { north = 0, east = 1, south = 2, west = 3, negZ = 0, posX = 1, posZ = 2, negX = 3 } ---@class PositionSidesBySide Position.sidesBySide = { [Position.side.negZ] = sides.negz, [Position.side.posX] = sides.posx, [Position.side.posZ] = sides.posz, [Position.side.negX] = sides.negx } ---@class PositionSideBySides Position.sideBySides = { [sides.negy] = Position.side.negZ, [sides.posy] = Position.side.negZ, [sides.negz] = Position.side.negZ, [sides.posx] = Position.side.posX, [sides.posz] = Position.side.posZ, [sides.negx] = Position.side.negX } ---@class PositionTurn Position.turn = { left = -1, forward = 0, right = 1, around = 2, back = 2 -- left = 3 } ---@param x number ---@param y number ---@param z number ---@param r PositionSide ---@return Position function Position:new(x, y, z, r) local instance = self.super:new() if not x then return self:extendedInstance(instance):set(0, 0, 0, 0) end return self:extendedInstance(instance):set(x, y, z, r) end ---@param adjacentPosition Position ---@return boolean,Sides @staus globalRotation:Sides_openOs function Position:getAdjacentSide(adjacentPosition) local this, adjacent, acc, check, result = self:get("yzx", true), adjacentPosition:get("yzx", true), 0, 0, 0 for i = 1, 3 do check = adjacent[i] - this[i] if check == 1 then result = i * 2 - 1 elseif check == -1 then result = i * 2 - 2 end acc = acc + check end return acc == 1 or acc == -1, result end ---@ changes self ---@param positionORx Position ---@param y number ---@param z number ---@return Position @self function Position:add(positionORx, y, z) if positionORx.class then self.x = self.x + positionORx.x self.y = self.y + positionORx.y self.z = self.z + positionORx.z return self end self.x = self.x + positionORx self.y = self.y + y self.z = self.z + z return self end ---@ changes self ---@param position Position ---@return Position @self function Position:sub(position) self.x = self.x - position.x self.y = self.y - position.y self.z = self.z - position.z return self end ---@ does not change sefl, args ---@param position Position ---@return number, number, number @x,y,zlua function Position:subCortage(position) return self.x - position.x, self.y - position.y, self.z - position.z end function Position:coordsEquels(position) return self.x == position.x and self.y == position.y and self.z == position.z end function Position:equels(position) return self == position or (self.x == position.x and self.y == position.y and self.z == position.z and self.r == position.r) end function Position:get(format, isPack) if format == nil then if isPack then return {self.x, self.y, self.z, self.r} end return self.x, self.y, self.z, self.r end if self:assertFormat(format) then local result = {} for key in format:gmatch '[xyzr]' do table.insert(result, self[key]) end if isPack then return result end return table.unpack(result) end end function Position:setf(format, ...) self:assertFormat(format) local args = table.pack(...) assert(#format <= #args, self:tostring() .. ":setf(format, ...) argument (...) very short") for i = 1, #format do self[format:sub(i, i)] = args[i] end return self end function Position:set(x, y, z, r) -- self.x, self.y, self.z, self.r = x, y, z, r -- return self return self:setf("xyzr", x, y, z, r) -- assert params end function Position:clone() -- local returnPos; returnPos = currentPos:clone() return self:new(self:get()) end function Position:copy(position) -- returnPos:copy(currentPos) return self:set(position:get()) end function Position:getCoordinatesUp() local x, y, z, r = self:get() return x, y + 1, z, r end function Position:getCoordinatesDown() local x, y, z, r = self:get() return x, y - 1, z, r end function Position:getCoordinatesLocalRotation(turn) self:getCoordinatesGlobalRotation((turn + self.r) % 4) end function Position:getCoordinatesGlobalRotation(side) local x, y, z, r = self:get() if side == self.side.negZ then z = z - 1 elseif side == self.side.posZ then z = z + 1 elseif side == self.side.negX then x = x - 1 elseif side == self.side.posX then x = x + 1 end return x, y, z, r end function Position:turnRight() self.r = (self.r + 1) % 4 return self end function Position:turnLeft() self.r = (self.r - 1) % 4 return self end function Position:turnAround() self.r = (self.r + 2) % 4 return self end function Position:stepUp() self.y = self.y + 1 return self end function Position:stepDown() self.y = self.y - 1 return self end function Position:stepForward() return self:stepBase(1) end function Position:stepBack() return self:stepBase(-1) end function Position:calculateTurn(globalRotation) return ((globalRotation - self.r) + 4) % 4 end ---@private function Position:assertFormat(format, message) if not format or type(format) ~= 'string' then format = type(format) -- if not massage then message = ' incorrect argument format type : ' .. format .. ' [xyzr] string only' -- end error(self:tostring() .. message) end if #format == 0 then -- if not massage then message = 'incorrect argument format : empty_string' -- end error(self:tostring() .. message) end if not massage then message = ' incorrect argument format : ' .. format .. ' [xyzr] only' end return assert(not format:match '[^xyzr]', self:tostring() .. message) end ---@private function Position:stepBase(dir) if self.r == self.side.negZ then self.z = self.z - dir elseif self.r == self.side.posZ then self.z = self.z + dir elseif self.r == self.side.negX then self.x = self.x - dir elseif self.r == self.side.posX then self.x = self.x + dir end return self end function Position:toStringDebug() return "x: " .. tostring(self.x) .. " y:" .. tostring(self.y) .. " z:" .. tostring(self.z) .. " r:" .. tostring(self.r) end function Position:print() print(self:toStringDebug()) return self end return Position -- -- Created by Clor#D on 17.05.2020. -- ©SofaSolt, 2020 -- ---@class Class ---@field public class string ---@field public super Class ---@field public isInstance boolean ---@field public clazz Class @ is not instanse class local Class if Class then return Class end Class = {} Class.class = "Class Class" Class.isInstance = false Class.super = Class Class.clazz = Class function Class:tostring() return tostring(self) .. " " .. self.class end function Class:extended (child) -- child = {} -- наследование статичестой части таблица Class, child.super доступ к статической таблице родителя child.super = self setmetatable(child, { __index = self, isInstance = false}) child.clazz = child return child end ---@class Interface ---@field public class string ---@param ... Interface function Class:implements(...) ---@type Interface[] local interfaceList = table.pack(...) for _,interface in ipairs(interfaceList) do interface.super = self.super setmetatable(interface, { __index = self.super, isInstance = false}) self.super = interface setmetatable(self, { __index = interface, isInstance = false}) end return self end function Class:extendedInstance(instance) -- наследование экземпляра таблица instance setmetatable(instance, self) self.__index = self return instance end function Class:new() local instance = {isInstance = true} return self.clazz:extendedInstance(instance) end function Class:noImplError(funcName) if not funcName then funcName = 'unknownFunc' end error(self:tostring() .. ':' .. funcName .. ' no implementation') end function Class:noImplIntefaceError(funcName, interfaceName) if not funcName then funcName = 'unknownIntefaceFunc' end error(self:tostring() .. ':' .. funcName .. ' no implementation '..interfaceName..":"..funcName) end function Class:assert(param, message) if not message then message = 'assert unknown param' end return assert(param, self:tostring() .. message) end function Class:assertNumberSetDefault(number, default, message)-- number = '100' if not message then message = 'assert unknown param' end if number == nil then if tonumber(default) then number = default else error(self:tostring() .. message .. " is nil") end end return assert(tonumber(number), self:tostring() .. message .. " not number") end function Class:assertNumber(number, message) return self:assertNumberSetDefault(number, nil, message) end function Class:assertEnum(param, enum, message) for _,element in pairs(enum) do if param == element then return param end end self:error(message.." param: "..tostring(param).." not listed") end function Class:error(message) error(self:tostring()..message) end function Class.nop() end function Class:getClassName() local _, name = string.gmatch(self.class, "([%a%d].+)%s(.+)")() return name end function Class:getClassLevel() local type = string.gmatch(self.class, "([%a%d].+)%s(.+)")() return type end function Class:assertInstance(message) if self.isInstance then return true end self:error(message) end function Class:assertSuperParentClass(param, sampleClass, message) ---@type Class local temp = param while temp.class ~= Class.class do if temp.class == sampleClass then return param end temp = temp.super end self:error(message.." "..param.class.." not extended "..sampleClass) end ---@param fieldName string ---@param fileldValue any function Class:createFieldIfMissing(fieldName, fileldValue) if self[fieldName] == nil then self[fieldName] = fileldValue end end return Class --[[ Using Class = require("/oop/Class") local Implementation if Implementation then return Implementation end Implementation = Class:extended({})-- "наследования" класса, по сути соеденение статических таблиц в иерархию наследования Implementation.class = "Implementation" function Implementation:toString() return self.class end function Implementation:new(stringParent, stringChildren) local instance = self.super:new(stringParent) -- конструктор предка self.stringC = stringChildren return self:extendedInstance(instance) -- "наследования" экземпляра, по сути соеденение таблиц экземляра в иерархию наследования end return Implementation ]] Изменено 2 мая, 2022 пользователем Clor Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах