Перейти к содержимому
Zer0Galaxy

Объектно-ориентированное программирование и Lua

Рекомендуемые сообщения

В данной статье я хотел бы показать, как можно применять объектно-ориентированный подход при программировании на 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]

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

post-6-14160784008126_thumb.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

 

Для тех кто все таки решил попробовать себя в объектно-ориентированном программировании, напоминаю — следите за пунктуацией. Имя поля отделяем точкой, имя метода — двоеточием. Это важно.

post-6-14160784008394_thumb.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

---Что получилось? Здесь, по идее и замыслу ООП, Гринго обладает всеми свойствами и методами класса Игроки, то есть наследует их,
---а также имеет свои свойтсва и методы класса Админы.

вот, что выдала программка:

 

DfOjWjr.png

 

Просьба, можно ли так же с объяснениями написать, если не затруднит о том, что я просил выше, на конкретном примерчике?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Лучше изучить ооп на Java, а потом применять знания эти в луа, так как тут псевдо ооп, изучать ооп в луа испортит все представление об ооп.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Лучше изучить ооп на Java, а потом применять знания эти в луа, так как тут псевдо ооп, изучать ооп в луа испортит все представление об ооп.

 

Это понятно. Изучать что-то можно всю жизнь, век живи, век учись, все равно нубом помрешь, как сказал Абдулла ибн Абдурахман. 

И это если ты по специальности программист.

 

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

 

Нео, это же игра просто.  Ты через чур ее близко к сердцу сильно прижал. За сервера переживаешь, браузеры на ОС и пинг стабильный чтобы был у твоей ОпенНет. 

 

Все равно что шутер-геймер спросил, какие перки на прокачку плазмогана есть и какие лучше пульки юзать, а ты ему говоришь.  Э-э-э-э-э-э-э, чува-а-а-ак, какие пульки, лучше в школу "Морских котиков" в Детройте записывайся и станешь мега-крутым снайпером, дадут пукалку настоящую :giggle:

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Вячеслав. Просьба! Напишите понятным языком, как оно должно выглядеть на примере.

Я ООП сам не открывал а искал в гугле. Я могу сделать статью из копипасты с гугла. Сойдет?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Я ООП сам не открывал а искал в гугле. Я могу сделать статью из копипасты с гугла. Сойдет?

Конечно сойдет!  В нете то любой полазить может и инфы там много всякой и винегрета.

Но важен пример конкретный. Вот, мол, готовый класс с наследованием для  программки для робота, печки, реактора и прочего(с комментариями в коде) Методы не нужны подробные. Важна структура только. Что за подчеркивания одинарные и двойные, что они значат, можно ли скрыть(запретить) изменение свойств вне класса, метаметоды, как уйти от двоеточий, я не сильно понял в той статье, что там за абракадабра. Ты же сам говоришь, что можно так сделать как-то и т.п.

 

У меня солдаты, некоторые не умели читать и писать толком, управляли многомиллионной по стоимости военной техникой высокоточной сложнейшей радиоэлектронной, подстраивали ее и выполняли боевые задачи повышенной важности и сложности по отработке нормативов и выдаче радиолокационной информации о воздушных целях и несли боевое дежурство. Но не имели понятия о транзисторах и законе Ома даже. А знаешь почему?  Потому что практически я им показал и рассказал, как и что делать, просто на пальцах объяснил и натренировал их до автоматизма, как обезьян. Понимаешь меня? :)

 

Думаю, что для ОпенГлассес вполне себе можно написать в стиле ООП несколько мощных классов и пусть народ их юзает на проекте нашем в свое удовольствие, так ведь?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Вот такой класс получился нормально?
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
]]

 

Изменено пользователем Clor

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в тему...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.


×
×
  • Создать...