Исходник Гайд class.lua - Полноценное ООП в Lua.

L00NEY

Участник
Автор темы
33
41

class.lua

class.lua - Это библиотека, которая добавляет несколько ключевых слов, с помощью которых можно легко создавать классы, и отказаться от функций конструкторов.


Как это работает?

В главном файле class.lua, находиться таблица, которая хранит в себе все объявленные классы, их поля, методы, и функцию - конструктор. Благодаря этому, библиотека сома понимает в каком контексте она работает сейчас, и с каким полем она работает сейчас. Это даёт возможность создать очень приятный и понятный синтаксис, будто это не библиотека, а все работает нативно.



Создание класса

Для создания класса, необходимо подключить библиотеку и воспользоваться ключевым словом class. Далее нужно указать имя нужного класса, и для красоты кода можно заключить весь body класса в do end
Lua:
require("class")

class "Car" do
   
end



Создание элементарных полей

В этой библиотеке, вы можете использовать область видимости полей, такие как public, private, static.
  1. Public - поле, которое будет видно всем. Поля public попадают в области видимости всех методов и в сам экземпляр класса. В методах обращения происходят через ключевое слово this.
  2. Private - поле, которое видят только лишь методы класса. Поля private не попадают в конечный экземпляр класса. В методах обращения происходит через ключевое слово privateThis.
  3. Static - поле, которое не может использовать в себе ключевые слова this и privateThis. Данное поле можно вызвать, ещё не создавая экземпляр самого класса. В нём можно хранить ключевую информацию, которая не будет никак изменяться в методах, и может быть использована в других классах.
Ниже пример создания класса Car, с публичным полем model:
Lua:
require("class") -- импорт class.lua

class "Car" do -- создание класса Car
 
    public "model" undefined() -- пишем область видимости (public), затем имя переменной, и указываем что не будем указывать в неё значение при создании (функция undefined())
 
end
В этом примере, мы не указали изначальное значение переменной. Если уж нам нужно указать значение переменной сразу, после её создания, нужно воспользоваться функцией define:
Lua:
require("class")

class "Car" do
 
    public "model" define "BMW M3"
 
end
[!] При использовании функции define , если вам нужно инициализировать строку или таблицу - вы можете не использовать скобки. Для всех других типов данных - нужно вызывать функцию только с скобками [!]



Создание экземпляра класса

Вот мы создали класс Car. Теперь нам необходимо создать его экземпляр, для работы с его полями. Сразу после объявления класса, в глобальную область видимости попадает таблица с именем самого класса. Эту таблицу можно использовать как конструктор экземпляра, и из неё же можно достать static методы
Lua:
require("class")

class "Car" do
 
    public "model" define "BMW M3"
 
end

local myCar = Car()
print(myCar.model) -- BMW M3
При создания экземпляра класса, в классе вызывается функция constructor. Она принимает в себя все аргументы что отправляются в конструктор. Представим что мы не указывали модель сразу, ибо хотим получить её из конструктора, это можно сделать очень легко:
Lua:
require("class")


class "Car" do

    public "model" undefined()
 
    constructor(function(carModel)
        this.model = carModel
    end)
 
end


local myCar = Car("BMW X4")
print(myCar.model) -- BMW X4
По сути, мы просто заворачиваем функцию, которую нам нужно вызвать при создании экземпляра, в constructor. Как и у всех методов, constructor так же имеет область видимости this - все публичные поля, и privateThis - область видимости приватных полей.



Создание методов

Создание метода не сильно отличается от создания поля. Для создания метода класса, нам необходимо использовать следующую конструкцию:
Lua:
require("class")


class "Car" do

    public "model" undefined()

    public() method "setModel" callback(function(newModel)
        this.model = newModel
    end)
 
    constructor(function(carModel)
        this.model = carModel
    end)

end

local myCar = Car("BMW X4")
print(myCar.model) -- BMW X4
myCar.setModel("BMW i8")
print(myCar.model) -- BMW i8
Мы используем функцию public чтобы показать область видимости следующего метода. Далее, через функцию method, мы указываем имя метода, а затем уже вызываем функцию callback, с нужной нам функцией в аргумент. Хочу обратить внимание, что при использовании такого подхода, использование двоеточия, при вызове методов не нужно.



Наследование

Допустим, мы хотим создать какой-нибудь другой класс, у которого будут свои поля, и какие-нибудь особые методы, но он так же должен сохранить все поля и методы другого класса. Для этого и нужно наследование. Наследование в class.lua создано следующим образом:
Lua:
require("class")


class "Car" do

    public "model" undefined()

    public() method "setModel" callback(function(newModel)
        this.model = newModel
    end)

    constructor(function(carModel)
        this.model = carModel
    end)

end

class "BMW" extends "Car" do
    constructor(function(model)
        super("BMW " .. model)
    end)
end

local myCar = Car("BMW X4")
print(myCar.model) -- BMW X4
myCar.setModel("BMW i8")
print(myCar.model) -- BMW i8

local bmwCar = BMW("X7")
print(bmwCar.model) -- BMW X7
Мы используем функцию extends , для того чтобы наследоваться от класса Car. И в функции constructor, мы вызываем функцию super, для того чтобы вызвать конструктор наследуемого класса.
Представим, что нам не нужно вызывать функцию конструктор, а нужно лишь просто изменить одно поле, в данном случае model. В этом случае, можно использовать функцию override:

Lua:
require("class")


class "Car" do

    public "model" undefined()

    public() method "setModel" callback(function(newModel)
        this.model = newModel
    end)

    constructor(function(carModel)
        this.model = carModel
    end)

end

class "BMW" extends "Car" do
    override "model" define "BMW X7" -- С помощью функции override указываем какое поле нужно перезаписать, и с помощью функции define указываем его значение
end

local myCar = Car("BMW X4")
print(myCar.model) -- BMW X4
myCar.setModel("BMW i8")
print(myCar.model) -- BMW i8

local bmwCar = BMW()
print(bmwCar.model) -- BMW X7

Тема и так получилась слишком большая, так что остальную информацию поместить не получиться. Если будут вопросы - можете задавать в тему.
 

Вложения

  • class.lua
    6.3 KB · Просмотры: 112
Последнее редактирование:

#Northn

Pears Project — уже запущен!
Всефорумный модератор
2,656
2,541
лять это гениально, хорошо замутил, красава...
явно лучше чем ооп в полисхелпер реборн!
1658240868837.png
 
  • Эм
  • Нравится
Реакции: L00NEY и Musaigen

L00NEY

Участник
Автор темы
33
41
update:
  1. new keyword "local class". This keyword returns a reference to the class and is not included in the global scope
    Lua:
    local someCar = localClass "Car" do  
      ...body
    end
    
    print(someCar) // "Car" metatable
    print(Car) // nil
  2. fixed bugs in "extends"
  3. fixed bugs in constructor
  4. fixed errors with "this" and "privateThis" when calling a method inside a method
 

L00NEY

Участник
Автор темы
33
41
update:

Decorators

Декораторы (Обёртка над методом) Вызываются до вызова самого метода, и могут использовать контекст его класса. Нужны декораторы, для того чтобы как-то влиять на поведение самого метода. Синтаксис:

Lua:
require("class")

function rightDirectionDecorator()
    this.direction = "right"
end

class "Car" do
    public "direction" define("forward")

    Decorators {
        rightDirectionDecorator
    }
    public() method "drive" callback(function()
        print(this.direction)
    end)

end

local car = Car()
car.drive() // right
 
  • Нравится
Реакции: neverlane

Ya Zaregalsya

Известный
386
134
Кроме вырвиглазной записи какие тут преимущества по сравнению с табличным ООП?
 

L00NEY

Участник
Автор темы
33
41
Кроме вырвиглазной записи какие тут преимущества по сравнению с табличным ООП?
почему вырвиглазной ? Синтаксис был построен так, чтобы было интуитивно понятно что ты делаешь. Многие имена функций и подходы были взяты из других языков программирования, во многом конечно эта библиотека вдохновлялась typescript. С этими классами ты получаешь возможность настроить область видимости своих полей, очень простое наследование, исчезает потребность в ненужном аргументе self, и исчезает потребность в двоеточиях при вызове методов
 

Ya Zaregalsya

Известный
386
134
почему вырвиглазной ? Синтаксис был построен так, чтобы было интуитивно понятно что ты делаешь. Многие имена функций и подходы были взяты из других языков программирования, во многом конечно эта библиотека вдохновлялась typescript. С этими классами ты получаешь возможность настроить область видимости своих полей, очень простое наследование, исчезает потребность в ненужном аргументе self, и исчезает потребность в двоеточиях при вызове методов
Если бы self был ненужным аргументом, то в C++ не было бы this. В двоеточии не вижу никаких проблем, наоборот позволяют быстрее сориентироваться в коде отличить метод от поля.
 
Последнее редактирование:

L00NEY

Участник
Автор темы
33
41
Если бы self был ненужным аргументом, то в C++ не было бы this. В двоеточии не вижу никаких проблем, наоборот позволяют быстрее сориентироваться в коде отличить метод от поля.
Как отсутствие двоеточия мешает отличить метод от поля ? Когда просто просматриваешь код? Там и так понятно, где метод, потому что скорее всего его вызывают. Когда пишешь код? Тоже не думаю, для Lua нормальных IDE так и нету, т.ч тебе в любом случае придётся смотреть в исходники самого объекта, ибо так ты не поймешь что метод, а что поле.

Аргумент self - действительно не нужен и не удобен. Доказательство моим словам, это языки, которые абсолютно полностью построенные на классах, главные примеры: C#, Java. Оба используют this keyword, как и эта библиотека
 

Ya Zaregalsya

Известный
386
134
Как отсутствие двоеточия мешает отличить метод от поля ? Когда просто просматриваешь код? Там и так понятно, где метод, потому что скорее всего его вызывают. Когда пишешь код? Тоже не думаю, для Lua нормальных IDE так и нету, т.ч тебе в любом случае придётся смотреть в исходники самого объекта, ибо так ты не поймешь что метод, а что поле.

Аргумент self - действительно не нужен и не удобен. Доказательство моим словам, это языки, которые абсолютно полностью построенные на классах, главные примеры: C#, Java. Оба используют this keyword, как и эта библиотека
Каким образом замена одного слова из 4-х букв на другое слово из 4-х букв может сделать его более нужным и удобным?)
 

L00NEY

Участник
Автор темы
33
41
Каким образом замена одного слова из 4-х букв на другое слово из 4-х букв может сделать его более нужным и удобным?)
речь же идёт не о замене, а получение его в аргументах. Тебе просто не нужно его получать он у тебя автоматически есть в глобальном скоупе.
Вообще глупо спорить по этому поводу. Использование ключевого слова, вместо аргумента это всего лишь небольшое решение именно этой библиотеки. А главная задача заключается в упрощении создания классов, наследовании.

Когда в обычных таблицах, тебе нужно создавать функции-конструкторы, использовать setmetatable для наследования, тут ты всё можешь реализовать в пару строк, и этот код получиться намного более читаемым
 
  • Нравится
Реакции: Ya Zaregalsya

Ya Zaregalsya

Известный
386
134
речь же идёт не о замене, а получение его в аргументах. Тебе просто не нужно его получать он у тебя автоматически есть в глобальном скоупе.
Вообще глупо спорить по этому поводу. Использование ключевого слова, вместо аргумента это всего лишь небольшое решение именно этой библиотеки. А главная задача заключается в упрощении создания классов, наследовании.

Когда в обычных таблицах, тебе нужно создавать функции-конструкторы, использовать setmetatable для наследования, тут ты всё можешь реализовать в пару строк, и этот код получиться намного более читаемым
Ну ок можно попробовать. В будущем надеюсь сделаешь что-то с названиями классов эти КАВЫЧКИ просто ужас. Переопределение методов родительского класса работает?
 
Последнее редактирование:

L00NEY

Участник
Автор темы
33
41
Ну ок можно попробовать. В будущем надеюсь сделаешь что-то с названиями классов эти скобки просто ужас. Переопределение методов родительского класса работает?
функция overrride

Lua:
override "field-name" callback(function()
    -- new method body
end)
 
  • Нравится
Реакции: Ya Zaregalsya