С помощью указанных механизмов достаточно просто реализовать подобие классов.
В итоге и получается нечто такое:
local MyClass = {} -- the table representing the class, which will double as the metatable for the instances MyClass.__index = MyClass -- failed table lookups on the instances should fallback to the class table, to get methods -- syntax equivalent to "MyClass.new = function..." function MyClass.new(init) local self = setmetatable({}, MyClass) self.value = init return self end function MyClass.set_value(self, newval) self.value = newval end function MyClass.get_value(self) return self.value end local i = MyClass.new(5) -- tbl:name(arg) is a shortcut for tbl.name(tbl, arg), except tbl is evaluated only once print(i:get_value()) --> 5 i:set_value(6) print(i:get_value()) --> 6
Всё это конечно хорошо, даже при определённой сноровке можно реализовать наследование…
Но где public и private члены класса? Дефакто в этом примере они все public. Да ещё и надо помнить, где использовать двоеточие:
MyClass:myFunc()
а где просто одну точку:
MyClass.myOtherFunc()
А статические члены класса? Неужели придётся отказываться?
Вот я и не захотел отказываться, и начал колхозить…
Итак, представляю вам мой колхоз:
createClass = function() local creator = {} creator.__private = { object_class = {}, } creator.__oncall = function(class_creator) -- Get the class definition so we can make needed variables private, static, etc. local this = class_creator() -- Initialize class from class definition __init = function() -- Init Public Static local class = {} if (type(this.__public_static) == "table") then class = this.__public_static end -- Init Object local thisClass = this local __constructor = function(...) local object = {} local this = class_creator() -- Init Public if (type(this.__public) == "table") then object = this.__public end -- Init static values of the class this.__public_static = thisClass.__public_static this.__private_static = thisClass.__private_static -- Call Constructor if (type(this.__construct) == "function") then this.__construct(...) end -- Returning constructed object return object end return {class = class, constructor = __constructor} end -- Creating class (returning constructor) local class_data = __init() local class = class_data.class local constructor = class_data.constructor -- Set class metatable (with setting constructor) local class_metatable = { __newindex = function(t, key, value) if type(t[key])=="nil" or type(t[key])=="function" then error("Attempt to redefine class") end rawset(t, key, value) end, __metatable = false, __call = function(t, ...) if type(t) == nil then error("Class object create failed!") end local obj = constructor(...) creator.__private.object_class[obj] = t local object_metatable = { __newindex = function(t, key, value) class = creator.__private.object_class[t] if type(class[key])~="nil" and type(class[key])~="function" then rawset(class, key, value) return end if type(t[key])~="nil" and type(t[key])~="function" then rawset(t, key, value) return end error("Attempt to redefine object") end, __index = t, __metatable = false, } setmetatable(obj, object_metatable) return obj end, } -- Setting class metatable to the class itself setmetatable(class, class_metatable) -- Returning resulting class return class end return creator.__oncall end createClass = createClass()
А пользоваться как? Очень просто, вот вам шаблон:
myclass_prototype = function() local this = {} this.__public_static = { -- Public Static Variables statvalue = 5, -- Public Static Funcs statfunc = function() print(this.__public_static.statvalue) end, } this.__private_static = { -- Private Static Variables privstatdat = 2, -- Private Static Funcs privstatfunc = function() print(this.__private_static.privstatdat) end, } this.__public = { -- Public Variables pubdata = 3, -- Public Funcs pubfunc = function(newprivate) print(this.__public.pubdata) this.__private.privdata = newprivate end, } this.__private = { -- Private Variables privdata = 1, -- Private Funcs listallprivate = function() print(this.__private.privdata) end, } this.__construct = function() -- Constructor end return this end myclass=createClass(myclass_prototype)
Как видите, при каждом вызове изнутри класса придётся каждый раз указывать путь, а ля «this.__private.privdata», зато вот вам пример использования созданного класса!
myobject = myclass() myobject.pubfunc(999)
При вызове этого кода будет создан объект myobject из класса myclass, и будет вызвана функция pubfunc, которая высветит содержимое публичной переменной и изменит приватную.
И никаких заморочек с двоеточиями!
Кстати, статические вызовы тоже работают. Как из класса, так и из объекта.
Итак, вкратце расскажу, что за магия здесь происходит. А происходит тут жонглирование так называемыми upvalues. upvalues — это переменные, которые изнутри видны, а снаружи — нет! Очень похоже на private, не так ли?
Так вот, создав функцию-«прототип», мы создали новую область видимости, и в неё поместили все внутренности нашего класса, вынеся наружу только public и public static члены класса. А всю остальную магию выполняют метатаблицы, которые позволяют определить, что именно будет происходить при запросе «несуществующего» члена внешней таблицы, которая представляет наш класс/объект.
Сумбурно звучит, знаю, но лучше не могу объяснить — не спец 🙂
Долго думал, как можно сделать наследование при такой системе, но так и не придумал — upvalues достаточно серьёзно ограничивает наши действия, а извращенствами вроде debug библиотеки пользоваться не хотелось — она не везде включена. Если кто додумается, буду рад увидеть!
PS: если для кого-то мой пост нечто очевидное — чтож, значит я себя переоценил 🙂 Не судите строго, может кому-то зачем-то пригодится это решение!
ссылка на оригинал статьи http://habrahabr.ru/post/182018/
Добавить комментарий