ООП lua

Published
» Способ реализации: Lua
» Тип: Наработка
» Версия Warcraft: 1.31+
Представляю Вам свою реализацию классов для lua. Классы создаются статически. На данный момент позволяет использовать области видимости static, public и override (private существует отдельно от наработки). Реализовано наследование. Основной задачей было организовать однородную структуру кода вне зависимости от выполняемой задачи и совместимость с EmmyLua (VsCode Lua от sumneko). Буду рад объективной критике. Исправлены ошибки и все объединено в одну область do-end.
» API
((
ClassAPI.new(name, parents)
Создание нового класса. Для созданного класса доступны для редактирования
только поля public, static и override. Для скрытия полей public и override рекомендуется
возвращать из области do-end поле static полученного класса.
name : string - имя нового класса.
parents : vararg - список родителей класса в порядке приоритетности.
return table
ClassAPI.allocate(class)
Создание инстанса класса.
class : table - класс созданный с помощью функции ClassAPI.new.
return table
ClassAPI.isClass(var)
Проверяет является ли переменная классом.
var : any
return boolean
ClassAPI.isInstance(var)
Проверяет является ли переменная инстансом любого класса.
var : any
return boolean
ClassAPI.getClass(instance)
Возвращает класс инстанса.
instance : table - инстанс класса созданный с помощью ClassAPI.allocate.
return table
ClassAPI.isChild(class1, class2)
Проверяет является ли класс наследником.
class1 : table - класс созданный с помощью функции ClassAPI.new.
class2 : table - класс созданный с помощью функции ClassAPI.new.
return boolean
ClassAPI.getPublic(static)
Позволяет получить поле public того же класса. Необходимо для реализации виртуальных функций.
static : table - поле класса "static"
return table - поле класс "public"
ClassAPI.getOverride(static)
Позволяет получить поле override того же класса. Необходимо для реализации статических
виртуальных функций.
static : table - поле класса "static"
return table - поле класс "override"
ClassAPI.type = ClassDeclare.type
))
Пример класса с использованием наработки:
» Action
--=========
-- Include
--=========

local lib_modname = Lib.current().modname

local Class = Lib.current().depencies.Class -- Равноценно local Class = ClassAPI

---@type LoggerClass
local Logger = require(lib_modname..'.Logger')
local Log = Logger.getDefault()
---@type UtilsFunctions
local Functions = require(lib_modname..'.Functions')
local checkType = Functions.checkType

--=======
-- Class
--=======

local Action = Class.new('Action')
---@class Action
local public = Action.public
---@class ActionClass
local static = Action.static
---@type ActionClass
local override = Action.override
local private = {}

--========
-- Static
--========

---@alias Callback fun(vararg:any[]):any

---@param callback Callback
---@param owner any
---@param child_instance Action | nil
---@return Action
function override.new(callback, owner, child_instance)
    checkType(callback, 'function', 'callback')
    if child_instance then
        checkType(child_instance, 'Action', 'child_instance')
    end

    local instance = child_instance or Class.allocate(Action)
    private.newData(instance, callback, owner)

    return instance
end

--========
-- Public
--========

function public:run(...)
    if private.debug then
        local success, result = pcall(private.data[self].callback, ...)
        if success then
            return result
        else
            Log:err(result)
        end
    else
        return private.data[self].callback(...)
    end
end

---@return any
function public:getOwner()
    return private.data[self].owner
end

--=========
-- Private
--=========

private.data = setmetatable({}, {__mode = 'k'})

private.debug = true

---@param self Action
---@param callback Callback
---@param owner any
function private.newData(self, callback, owner)
    local priv = {
        callback = callback,
        owner = owner
    }
    private.data[self] = priv
end

return static
» Функция checkType
---@param var any
---@param need_type any
---@param var_name string
---@param level number | nil
function UtilsFunctions.checkType(var, need_type, var_name, level)
    if not debug then
        return
    end

    local real_type = type(var)
    if real_type == 'userdata' then
        local wc3_type_string = tostring(var)
        local pos = wc3_type_string:find(':')
        local wc3_type = wc3_type_string:sub(1, pos - 1)
        if wc3_type ~= need_type then
            Log:err('variable \''..(var_name or '')..'\'('..wc3_type..') is not of type '..tostring(need_type), level or 3)
        end
        return
    end

    if not ClassAPI.classType(var, need_type) then
        Log:err('variable \''..(var_name or '')..'\' is not of type '..tostring(need_type), level or 3)
    end
end
» Использование:
local action = Action.new(function(a, b) return a/b end)
action:run(1, 0)

--[[ Будет выведена ошибка деление на нуль с указание номера строки "local action = Action.new(function(a, b) return a/b end)" ]]
Класс Action позволяет безопасно запускать функции при private.debug = true. Использую его для действий триггеров, таймеров и т.д, что позволяет оперативно найти ошибку в коде.


Views: 1 061

» Лучшие комментарии


ScorpioT1000 #1 - 10 months ago 0
Голосов: +0 / -0
Опередил)
На самом деле, стоит адекватно уделить время этой теме и написать серьезные обучающие статьи на этот счёт.
Nelloy #2 - 10 months ago (изм. ) 0
Голосов: +0 / -0
ScorpioT1000, да, было бы неплохо всю инфу по lua разжевать и залить в одно место. А я только пару дней назад узнал, что у функции error есть второй параметр, который крайне необходим в реалиях wc3
PT153 #3 - 10 months ago 0
Голосов: +0 / -0
Стоит убрать старые статьи, либо чутка обновить их, то же введение в JASS.
Bergi_Bear #4 - 10 months ago 0
Голосов: +0 / -0
А можно немножечко для тех кто в танке и не понимает зачем это вообще нужно:
  1. Что это даст?
  2. Тем кто не понимает, нужно ли это изучать?
  3. Что те кто не понимают этого, что они теряют?
ScorpioT1000 #5 - 10 months ago 0
Голосов: +0 / -0
Bergi_Bear, в процедурном программировании даже при максимально красивом коде приходит момент, когда начинается кошмар и путаница

Хотя_можно_писать_много_префиксов_func()
Nelloy #6 - 10 months ago (изм. ) 0
Голосов: +0 / -0
ScorpioT1000, в луа это можно решить либами на основе таблиц. Тут больше в мировоззрении дело и в поставленной задаче. Где-то удобнее ООП, где-то функциональное...
NazarPunk #8 - 10 months ago 3
Голосов: +3 / -0
Что это даст?
В вашем коде будет ООП ради ООП
Тем кто не понимает, нужно ли это изучать?
В таких наработках нужно смотреть на примеры - если они сферические в вакууме, то скорей всего ООП не решает задачи, а создаёт проблемы.
в процедурном программировании даже при максимально красивом коде приходит момент, когда начинается кошмар и путаница
ООП не серебрянная пуля и не лечит кошмар и путаницу.
Тут больше в мировоззрении дело и в поставленной задаче.
Я так понимаю, задача была реализовать ООП в луа?
ScorpioT1000 #9 - 10 months ago (изм. ) 0
Голосов: +0 / -0
ООП подразумевает инкапсуляцию и при этом возможность расширения, т.е. даже если понадобится изменить большой 3rd party функционал и только для конкретного места, менять придется минимум - унаследовался и переписал метод.
PT153 #10 - 10 months ago 0
Голосов: +0 / -0
Заметил функцию require. Её отключили в игре.
NazarPunk #11 - 10 months ago (изм. ) 0
Голосов: +0 / -0
ООП подразумевает инкапсуляцию
do end тоже подразумевает инкапсуляцию.
при этом возможность расширения, т.е. даже если понадобится изменить большой 3rd party функционал и только для конкретного места, менять придется минимум - унаследовался и переписал метод.
В языках заточенных под ООП в этом резон есть, только зачем тащить это в lua?
А я только пару дней назад узнал, что у функции error есть второй параметр, который крайне необходим в реалиях wc3
Как это вы умудряетесь пользоваться функциями без документации?
Прикрепленные файлы
ScorpioT1000 #12 - 10 months ago (изм. ) 0
Голосов: +0 / -0
do end тоже подразумевает инкапсуляцию.
Путаешь с областью видимости. Следовательно, ты ничего не понимаешь в ООП, продолжать смысла не вижу) иногда полезно признавать, что ничего не понимаешь в каких-то вещах
PT153 #13 - 10 months ago (изм. ) 1
Голосов: +1 / -0
» Что такое инкапсуляция
Инкапсуляция (англ. encapsulation, от лат. in capsula) — в информатике размещение в одном компоненте данных и методов, которые с ними работают. Также может означать скрытие внутренней реализации от других компонентов. Например, доступ к скрытой переменной может предоставляться не напрямую, а с помощью методов для чтения (геттер) и изменения (сеттер) её значения.
Инкапсуляция зачастую рассматривается как понятие, присущее исключительно объектно-ориентированному программированию (ООП), но в действительности обширно встречается и в других. В ООП инкапсуляция тесно связана с принципом абстракции данных (не путать с абстрактными типами данных, реализации которых предоставляют возможность инкапсуляции, но имеют иную природу). Это, в частности, приводит к другому распространённому заблуждению — рассмотрению инкапсуляции неотрывно от сокрытия. В частности, в сообществе С++ или Java принято рассматривать инкапсуляцию без сокрытия как неполноценную. Однако, некоторые языки (например, Smalltalk, Python) реализуют инкапсуляцию в полной мере, но не предусматривают возможности скрытия в принципе. Другие (Standard ML, OCaml) жёстко разделяют эти понятия как ортогональные и предоставляют их в семантически различном виде.
Как это вы умудряетесь пользоваться функциями без документации?
Да запросто. Где-то у какого-нибудь назарпанка увидел и себе скопировал)
NazarPunk #15 - 10 months ago (изм. ) 1
Голосов: +1 / -0
Путаешь с областью видимости.
Почему сразу путаю?
Следовательно, ты ничего не понимаешь в ООП, продолжать смысла не вижу)
Я статью на вики прочитал, так что понимаю)

А вот зачем оно в варкрафте уже не понимаю.
Nelloy #16 - 10 months ago 0
Голосов: +0 / -0
NazarPunk, да, ООП ради ООП. Да он только создает лишнюю нагрузку и да в wc3 можно спокойно жить без него. Вообще ООП нахер не нужен и иногда только усложняет. Мне так проще видеть структуру проекта и определять необходимый функционал, плюс это ограничивает некоторые мои ошибки. Зачем используется, например, glib? Который вообще весьма уродлив, на мой взгляд.
ScorpioT1000 #17 - 10 months ago (изм. ) 0
Голосов: +0 / -0
Действительно, зачем это в варкрафте (это чисто либы, в которых бывают немалые наборы классов)