Comments 13
> а где просто одну точку

Везде можно писать двоеточие, так как двоеточие это «сахар»: Объект.функция(Объект, параметры) == Объект: функция(параметры). (upd: тут парсер всунул лишний пробел)

Ваша реализация классов немного запутана: статик, паблик, приват и всё отдельными таблицами. У вас же есть основная таблица — вот вам и статик, вот вам и паблик. Хорошая идея, как мне кажется, хранить скрытые свойства в метаданных. Их никто не увидит и доступа к ним никто не получит, если сам «силой» не влезет через getmetatable(), но так же можно читать и userdata. Получается в духе Lua.

Наследования можно осуществлять двумя путями:
1. через какую-нибудь функцию extends('MyClass', родитель);
2. через обращение к окружению: [local] MyClass = my_class_system(родитель);

Второй метод интересен и хитёр, но нужно копаться с getfenv() и debug.getlocal(). И далеко не факт, что имена переменных не потеряются.
Тут в чём особенность. Дело всё в том, что у каждой таблицы (статик паблик, статик приват, просто паблик, просто приват) выставляется разная видимость. Потому и четыре разных таблицы — каждой аккуратно присваиваются свои права доступа. Если же доступ к основной таблице открывать, то тогда можно и до приватных добраться, чего я хотел избежать абсолютно.

Двоеточие в принципе почему-то не понравилось, так то я прекрасно знаю, что можно всё через двоеточие писать (типичный сахар), но можно сказать, что челлендж был как раз от них избавиться) Мол, можно ли добиться того же без оного сахара?

К getmetatable можно и закрыть доступ (__metatable = false). Думал как раз метатейблы использовать для наследования, но что-то не додумался как, не получилось. Да и дефакто метатейбл получается такой же скрытой Upvalue таблицей, её можно и не только при помощи метатейбла сделать. Впрочем, тут в принципе подход с upvalues достаточно жёстко ограничивает свободу действий.

Насчёт getfenv() и debug.getlocal() — первый в lua 5.2 вообще вырезан, а вот debug бывает и отключён (debug = nil, особенно в каких нить embedded вариантах луа).

Вообще, хотелось бы увидеть вашу реализацию, может и смог бы чего нибудь почерпнуть.
Точно, в Lua 5.2 getfenv заменили на _ENV.

Я могу скинуть в личку ссылку на свой говнокод годичной давности, если интересно.

P.S. Иногда, когда debug == nil явный require('debug') выручает ситуацию. Из-за того, что вырезают luaopen_debug() из автоматического подключения стандартных модулей.

upd: прошу прощения, промахнулся.
Зачем нужен весь этот колхоз не очень понятно:
1. Есть готовые реализации, например, вот хорошая github.com/kikito/middleclass/
2. Lua предполагает несколько другую парадигму вместо ООП, вот ей и надо пользоваться.

А так получилась статья из серии «а смотрите как я могу». Можете, молодец. Но зачем это здесь? Кому нужен этот непонятный код?

> Сумбурно звучит, знаю, но лучше не могу объяснить — не спец :)
> Не судите строго, может кому-то зачем-то пригодится это решение!

Ноу коммент. Филиал ЖЖ открыт?
Хочу лишь поблагодарить за ссылку и отметить правильность Вашего подхода (на тему парадигмы lua). Со всем остальным предпочту не согласиться.
Целью моей попытки было проверить, возможно ли это, или нет, и насколько возможно. И я считаю, что своей статьёй кому-то помог, возможно даже навёл на мысль — мне большего и не нужно.

PS: лично я считаю, что уж лучше такая статья, чем бесконечный поток малополезных новостей. Ну и смотрите хабы и теги.
> И я считаю, что своей статьёй кому-то помог

Как можно помочь плохой статьёй? Как можно помочь кодом, для понимания которого даже хорошему программисту надо затратить заметное время для понимания? Вы выложили код, почти без объяснения и думаете что это кому-то поможет? Тот кто может в нем сейчас разобраться — может наколхозить такого over 9000.

И я даже не говорю ещё про то, что так в lua лучше не делать и про вред велосипедостроения.

> что уж лучше такая статья, чем бесконечный поток малополезных новостей

Это плохая позиция. «Остальные выкладывают трешак, ну и я выложу тоже почему нет».
Я готов в меру своих возможностей ответить на вопросы по выложенному коду при необходимости. Пока вопросов нет — наверное, не всё так плохо, как Вам кажется.

Лично я считаю, что lua настолько мощный язык, что в нём реально реализовывать самые разные парадигмы, в том числе классический ООП, что и пытаюсь показать этой статьёй. Опять таки, замечу, что неспроста разместил данную статью в блок «Ненормальное программирование» — ибо согласен, что всё это велосипедостроение и нарушение «парадигмы lua». Но… Ну и что! Возможно, не мне одному придётся как-нибудь реализовывать нечто подобное, вот и возможно этот код немного поможет реализовать нечто иное, но схожее.

Дискуссия идёт в никуда, предлагаю её свернуть.
На практике все эти нагромождения искусственных классов мало нужны.
Table вполне себе объект. И очень просто реализуются в нём private и public.
Вот мой пример:
local _M = {} -- module table
 
_M.someProperty = 1 -- class properties
 
local function createText()
   -- local functions are still valid, but not seen from outside - "private"
end
 
local privateVar -- so do local variables
 
_GLOBAL_VAR -- without local it's global
 
function _M.staticMethod(vars)
    -- this is class method like function (dot)
    -- there is no "self"
end
 
function _M:someMethod(vars)
    -- this is object method like function (colon)
    -- there is "self"
end
 
function _M:newBaseObject()
    -- Here goes constructor code
    local object = {}
    object.vars = 'some vars'
    object.name = 'BaseObject'
    object.property = self.someProperty -- from module

    local privateObjectVar = 'my_secret' -- private, can't be seen from outside
 
    function object.staticMethodInc(i) -- without colon it's "static"
        return i + 1
    end
    
    function object:sign(song)
        print(self.name .. ' is singing ' .. song)
    end

    function object:destroy()
       -- optional destructor, after this to delete an object you just need to remove all references to it
       self.vars = nil
    end
 
    return object
end
 
-- Now inheritance
function _M:newChildObject()
    local object = self:newBaseObject()
    -- override any methods or add new
    object.name = 'ChildObject'
    function object:tell(story)
        print(self.name .. ' is telling ' .. story)
    end
    return object
end
 
return _M -- return this table as a module to require()

После загрузки этого модуля объекты создаются простым вызовом функций
local MyNewObject = thatModule:newChildObject()
Моя цель была как раз избавиться от двоеточия :) Так-то всё абсолютно верно.
Да и смущает меня local privateVar — это вы объявляете видимость в пределах файла. Если несколько классов в одном файле могут быть проблемы. Например для того же наследованного класса здесь вы не можете задать свои собственные статические переменные — они тот час же станут общими и для родительского, что считаю недопустимым. Впрочем кому как нравится.

Да и ИМХО мой результирующий шаблон создания класса выглядит проще.

На самом деле появилась идея и как с моим способом реализовать наследование, но это я уже буду экспериментировать, видимо, вне хабра.
>> Моя цель была как раз избавиться от двоеточия
Ужас. Двоеточие как раз помогает.

local privateVar — это именно private для модуля. Private для объекта это local privateObjectVar чуть ниже. Так что всё нормально с видимостью.

Проще? Ну, хорошо, вот шаблон без всего лишнего:
local function newObject()
    local object = {}
    object.publicProperty = 'Some property'
    local privateVar = 'my_secret'
 
    function object:someMethod(value)
        print(self.publicProperty, privateVar, value)
    end

    function object:destroy()
       self.publicProperty = nil
       privateVar = nil
    end
 
    return object
end

Проще некуда.
Тут необходимо выносить весь класс в отдельный файл, чего так же не хотелось делать. local действует в текущем файле.
И таки да. Хоть двоеточие и «помогает», но плодит сущности. ИМО.
Рад :) Если что-то доработали, то было бы интересно услышать что. :)
Only those users with full accounts are able to leave comments. Log in, please.