RccHD 136 Опубликовано: 22 июня, 2017 (изменено) Предисловие Во многих языках программирования классы включены в стандартную реализацию. Однако Lua настолько минималистичен, что по умолчанию классов в нем нет. Но нам ничего не мешает реализовать классы используя стандартные возможности языка. В моей реализации классы реализованы через замыкания. Описание моей библиотеки Здесь особо описывать нечего, поэтому я просто приложил примеры кода( комментарии обязательны к прочтению! ) 1. Создание класса local Class = require("class") -- Класс для точки в 2д пространстве local Point2d = Class({ -- Конструктор класса __init__ = function(self, x, y) self.x, self.y = x, y end, -- Методы класса -- Примечание: -- аргументы каждого метода класса обязательно -- должны начинаться с "self" (ну, либо с "this" или "it", кому как угодно) printCoords = function(self) print("X = ".. self.x .."; Y = ".. self.y) end }) -- Создание экземпляра класса Point2d local p1 = Point2d(5, 11) -- Вызов метода p1.printCoords() -- Выведет "X = 5; Y = 11" local p2 = Point2d(p1.x * 2, p1.y * 2) p2.printCoords() -- Выведет "X = 10; Y = 22" 2. Ты ведь не забыл про наследования?!Я не очень люблю нагромождать свой код зависимостями, а потом постоянно ломать голову мыслями "А от чего у меня унаследован вот этот класс? От какого именно класса у меня унаследован этот метод? Почему одно наследование перекрывает другое? "Поэтому я реализовал возможность выборочно унаследовать необходимый метод у конкретного класса, так как такой способ наследования тупо проще, понятнее, и лишний раз не нагружает разум программиста адскими зависимостями между классами.Простой пример: local Class = require("class") -- Класс для точки в 2д пространстве local Point2d, Point2d_prototype = Class({ __init__ = function(self, x, y) self.x, self.y = x, y end, printCoords = function(self) print("X = ".. self.x .."; Y = ".. self.y) end }) local Player = Class({ __init__ = function(self, x, y, ...) self.x, self.y = x, y self.otherInfo = ... end, -- Унаследуем метод "printCoords" у класса Point2d -- Для этого обратимся к *прототипу* этого метода и получим наследуемый метод по имени printCoords = Point2d_prototype.printCoords }) -- Создание экземпляра класса Point2d local point = Point2d(5, 11) -- Вызов метода point.printCoords() -- Выведет "X = 5; Y = 11" local player = Player(4, 2, "RccHD") player.printCoords() -- Выведет "X = 4; Y = 2" Исходники библиотеки Библиотека получилась совсем небольшой, поэтому вы можете скопировать исходник прямо из этой статьи:Либо скачать с pastebin ( https://pastebin.com/WVxaHb4Y )class.lua : local prepareObj prepareObj = function(root, obj, proto, mt) for k, v in pairs(proto) do local t = type(v) if t == "table" and k ~= "__proto__" and k ~= "__root__" then obj[k] = prepareObj(root, { __proto__ = v, __root__ = root }, v, mt) end end setmetatable(obj, mt) return obj end local classMt = { __index = function(self, key) local v = self.__proto__[key] if type(v) == "function" then return function(...) return v(self.__root__, ...) end else return v end end } return function(proto) proto.__new__ = function(...) local obj = { __proto__ = proto } obj.__root__ = prepareObj(obj, obj, proto, classMt) if proto.__init__ ~= nil then proto.__init__(obj.__root__, ...) end return obj end return proto.__new__, proto end Для того, чтобы начать использовать классы в своих программах нужно положить файл class.lua в папку рядом со своими программами (или в /usr/lib , но лучше не надо)После этого импортируйте библиотеку написав в своем коде вот это: local Class = require("class") И помните: важно не количество кода, а его качество! Изменено 21 сентября, 2017 пользователем RccHD 3 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
RccHD Автор темы 136 Опубликовано: 23 июня, 2017 (изменено) АХ, ДА, СОВСЕМ ЗАБЫЛЕсли вдруг у кого-то возник вопрос "а где private? а где public? а где local private protected?", то вот вам реализация ваших любимых private и public 1. public (публичные параметры, которые доступны другим классам) указываются в функции __init__ . То есть в конструкторе класса(все очень просто)2. private (в моей библиотеке не реализованы области видимости для "приватных" параметров, но зато такие области видимости реализованы в Lua)Это намек на то, что не нужно усложнять код, а вместо этого стоит использовать уже реализованные области видимости языка LuaДля этого нужно обернуть код класса в do..end, таким образом создав для этого класса изолированную область видимостиПример: local Class = require("class") local myObject do local secretString = "это приватная меременная без всяких заморочек" myObject = Class({ -- класс без конструктора publicString = "а это публичная переменная!!!" }) end -- Создание экземпляра класса myObject local obj = myObject() print(obj.publicString) -- Выведет "а это публичная переменная!!!" print(obj.secretString) -- выведет "nil", так как это приватная переменная класса 3. А любителям всяких там "local private protected friendly" и прочих извращенских протектед-статиков (привет Java и C++) я ничего не могу предложить. Так как я не решился реализовывать настолько извращенские вещи, хотя бы потому что я не мазохист и никогда не стану это использовать на практикеЛично мне простых public и private хватаетНадеюсь я не задел ни чьи религиозные чувства! Изменено 23 июня, 2017 пользователем RccHD Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Zer0Galaxy 2 187 Опубликовано: 23 июня, 2017 Получается, если у класса-родителя двести пятьдесят методов и класс-наследник хочет их всех унаследовать, все двести пятьдесят нужно перечислить в объявлении наследника? А нельзя это как-то автоматизировать? Например, передавать прототип родителя в функцию Class() вторым параметром, чтобы она занималась наследованием методов. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Fingercomp 4 409 Опубликовано: 23 июня, 2017 Дааа... Я вот тоже такой фигнёй маялся неделю назад — и вместо того, чтобы завести нормальные классы: с многоуровневым множественным наследованием, красивым синтаксисом, объявлением мета-методов прямо в классе без необходимости плясать с бубном, конструкторами/деструкторами, сеттерами/геттерами, супер-вызовами, геттерами/сеттерами и прочим — у меня классы делались похожим образом: создаватель классов возвращал функцию-конструктор. И довольно быстро упёрся в разные проблемы с этим: например, я хочу, не создавая инстанс класса, создать кастомный класс, чтобы заменить какую-то константу в исходном классе. Надо унаследоваться и заменить. У меня надо было таскать мета-таблицу, у тебя — прототип. А в той вкусной либе, которую я описывал, достаточно вот такого кода: local CustomClass = newClass(BaseClass) CustomClass.SOME_CONSTANT = 42 И затем можно юзать кастомный класс с переопределённым атрибутом. По поводу приватных методов: функцию создавать — это абсолютно лишний оверхэд. Scope в луа создаётся блоком do ... end. Например: local myObject do local secret = "abc" myObject = Class({ public = "hey" }) end Ну и буквы как-то великоваты будут. Вроде не настолько плоховидящие тут люди сидят. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Seryoga 184 Опубликовано: 23 июня, 2017 (изменено) Чем вам не понравилось стандартное исполнение классов в lua? Можно ли в runtime узнать является ли объект чьим-либо наследником или нет? Создание приватных полей и методов похоже на один огромный костыль. Почему если вы не понимаете зачем нужны те или иные фишки языка, то человек который их использует -- извращенец? Мне кажется вы никогда и ничего не писали на c++ или java. Основное приимущество слов private, public, protected и тп. это статическая или очень ранняя динамическая проверка наличия ошибок в программе. static нужен для создания общей памяти для всех классов. Также эти маркеры помогают размечать память объекта, но для луа это не актуально. Насчёт извращенцев, когда вам потребуется наследовать не все методы из родительского класса? Знаете что такое singleton? Как его создать используя вашу библиотеку? Как создать несколько singleton'ов в одном пакете? Есть класс MyClass. Нужно создать метод класса, который создаёт новый объект класса MyClass и что-то с ним делает. Как это сделать? Как переопределить операторы +, -, / и тп.? Нельзя ли избавиться от двойной скобки, используя vararg? local Player = Class({ ... }) Какой код сложнее? local myObject = (function() local secretString = "это приватная меременная без всяких заморочек" return Class({ -- класс без конструктора publicString = "а это публичная переменная!!!" }) end)() или local MyClass = Class({ "public", public_field = "abc", "private", private_field = "def" }) Как думаете почему создатели языка lua отказались от классов? Изменено 23 июня, 2017 пользователем Seryoga Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
RccHD Автор темы 136 Опубликовано: 23 июня, 2017 Получается, если у класса-родителя двести пятьдесят методов и класс-наследник хочет их всех унаследовать, все двести пятьдесят нужно перечислить в объявлении наследника? А нельзя это как-то автоматизировать? Например, передавать прототип родителя в функцию Class() вторым параметром, чтобы она занималась наследованием методов. Хорошая идея. Наверное так и сделаю Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
RccHD Автор темы 136 Опубликовано: 23 июня, 2017 (изменено) И довольно быстро упёрся в разные проблемы с этим: например, я хочу, не создавая инстанс класса, создать кастомный класс, чтобы заменить какую-то константу в исходном классе. Надо унаследоваться и заменить. У меня надо было таскать мета-таблицу, у тебя — прототип. А в той вкусной либе, которую я описывал, достаточно вот такого кода: local CustomClass = newClass(BaseClass) CustomClass.SOME_CONSTANT = 42 И затем можно юзать кастомный класс с переопределённым атрибутом. А в чем собственно проблема? Вот так это делается: local customClass = function(...) local obj = BaseClass(...) obj.SOME_CONSTANT = 42 return obj end Изменено 23 июня, 2017 пользователем RccHD Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
RccHD Автор темы 136 Опубликовано: 23 июня, 2017 (изменено) Чем вам не понравилось стандартное исполнение классов в lua? С чего вы взяли что мне оно не нравится? Оно скорее вам не нравится, так как там нет public static protected friendly Можно ли в runtime узнать является ли объект чьим-либо наследником или нет? Нет, потому что в моей реализации нет наследования в том виде, к которому вы привыкли. В моей реализации можно выборочно наследовать конкретные методы Создание приватных полей и методов похоже на один огромный костыль. Чего ж вы так не любите Lua, что стандартные области видимости языка вам кажутся костылями? Почему если вы не понимаете зачем нужны те или иные фишки языка, то человек который их использует -- извращенец? Мне кажется вы никогда и ничего не писали на c++ или java. Основное приимущество слов private, public, protected и тп. это статическая или очень ранняя динамическая проверка наличия ошибок в программе. static нужен для создания общей памяти для всех классов. Также эти маркеры помогают размечать память объекта, но для луа это не актуально. Те, кто пишут на C++ и Java пусть используют private и protected и т.п. В таком языке как Lua private и protected выглядят просто по-извращенски Насчёт извращенцев, когда вам потребуется наследовать не все методы из родительского класса? Представьте себе, что есть объекты Rect, Triangle и Line. У всех этих объектов может быть общий метод Rotate(x, y, z, angle), который будет унаследован у Figure_prototype.Rotate Знаете что такое singleton? Как его создать используя вашу библиотеку? Как создать несколько singleton'ов в одном пакете? В Lua это не нужно. Есть класс MyClass. Нужно создать метод класса, который создаёт новый объект класса MyClass и что-то с ним делает. Как это сделать? Если коротко, то вот так local myClass = Class({ __init__ = function(self, x) self.x = x end, strange_method = function(self, y) local myObj = self.__new__(y) myObj.desc = "Объект типа myClass" return myObj end }) Изменено 23 июня, 2017 пользователем RccHD Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Fingercomp 4 409 Опубликовано: 23 июня, 2017 (изменено) [тут была цитата, но её уже исправили] Нельзя ли избавиться от двойной скобки, используя vararg? Если функция вызывается с одним аргументом, представленным строковым литералом или табличным, то круглые скобки можно опустить при вызове. Короче: -- эти строки эквивалентны: func({"test"}) func {"test"} -- и эти: func("test") func "test" В Lua это не нужно. Ничего себе: "не нужно". Очень даже нужно было мне, например. Изменено 23 июня, 2017 пользователем Fingercomp Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
RccHD Автор темы 136 Опубликовано: 23 июня, 2017 Ничего себе: "не нужно". Очень даже нужно было мне, например. Ну и как вы бы реализовали singleton? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Totoro 3 563 Опубликовано: 23 июня, 2017 В Lua это не нужно. Предлагаю расширить цитату до: "ООП в Луа не нужно." :P Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
RccHD Автор темы 136 Опубликовано: 23 июня, 2017 Предлагаю расширить цитату до: "ООП в Луа не нужно." :P Я имел в виду, что реализовывать singleton в Lua не нужно Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Seryoga 184 Опубликовано: 23 июня, 2017 (изменено) Seryoga сказал(а) 23 Июн 2017 - 11:53: Насчёт извращенцев, когда вам потребуется наследовать не все методы из родительского класса? Представьте себе, что есть объекты Rect, Triangle и Line. У всех этих объектов может быть общий метод Rotate(x, y, z, angle), который будет унаследован у Figure_prototype.Rotate Это вы рассказываете где нужно наследование, а не о том где не нужно наследовать все методы. Изменено 23 июня, 2017 пользователем Seryoga Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
RccHD Автор темы 136 Опубликовано: 23 июня, 2017 (изменено) Это вы рассказываете где нужно наследование, а не о том где не нужно наследовать все методы. каждый понимает по-своему Вы считаете, что нужно все методы наследовать, а я считаю что не обязательно наследовать именно все методы. Лучше выборочно Изменено 23 июня, 2017 пользователем RccHD Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Arseniy10 50 Опубликовано: 24 июня, 2017 каждый понимает по-своему Вы считаете, что нужно все методы наследовать, а я считаю что не обязательно наследовать именно все методы. Лучше выборочно Можно же просто добавить функцию extendsAll и все дела. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
m0cesta 14 Опубликовано: 25 июня, 2017 (изменено) Чем вам не понравилось стандартное исполнение классов в lua? Можно ли в runtime узнать является ли объект чьим-либо наследником или нет? Создание приватных полей и методов похоже на один огромный костыль. Почему если вы не понимаете зачем нужны те или иные фишки языка, то человек который их использует -- извращенец? Мне кажется вы никогда и ничего не писали на c++ или java. Основное приимущество слов private, public, protected и тп. это статическая или очень ранняя динамическая проверка наличия ошибок в программе. static нужен для создания общей памяти для всех классов. Также эти маркеры помогают размечать память объекта, но для луа это не актуально. Насчёт извращенцев, когда вам потребуется наследовать не все методы из родительского класса? Знаете что такое singleton? Как его создать используя вашу библиотеку? Как создать несколько singleton'ов в одном пакете? Есть класс MyClass. Нужно создать метод класса, который создаёт новый объект класса MyClass и что-то с ним делает. Как это сделать? Как переопределить операторы +, -, / и тп.? Нельзя ли избавиться от двойной скобки, используя vararg? local Player = Class({ ... }) Какой код сложнее? local myObject = (function() local secretString = "это приватная меременная без всяких заморочек" return Class({ -- класс без конструктора publicString = "а это публичная переменная!!!" }) end)()или local MyClass = Class({ "public", public_field = "abc", "private", private_field = "def" }) Как думаете почему создатели языка lua отказались от классов? Public, private, protected это ущербные костыли, необходимые для того, чтобы мы выбирали какие поля и методы наследовать. У RccHD это все решается простой возможностью наследовать только то что мы хотим. Про остальной бред я даже говорить не хочу. Изменено 16 сентября, 2018 пользователем eu_tomat spoiler Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
NEO 541 Опубликовано: 25 июня, 2017 Если я хочу использовать ООП, я использую moonscript, профит. 3 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
TC1061 14 Опубликовано: 19 августа, 2017 И довольно быстро упёрся в разные проблемы с этим: например, я хочу, не создавая инстанс класса, создать кастомный класс, чтобы заменить какую-то константу в исходном классе. Надо унаследоваться и заменить. У меня надо было таскать мета-таблицу, у тебя — прототип. А в той вкусной либе Та вкусная либа даже слишком вкусная! Притерная . Надо опробовать. А может и нет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
RccHD Автор темы 136 Опубликовано: 17 сентября, 2017 Я немного доработал библиотеку class.luaСсылка на скачивание не изменилась:https://pastebin.com/WVxaHb4Y Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах