Прототипное ООП для Lua

    Привет, я придумал свой велосипед для реализации прототипного подхода ООП в Lua.

    Основные фишки
    • Single inheritance
    • Traits
    • LuaJIT


    Перейдем сразу к примерам.

    -- подключаем модуль
    local object = require("object")
    
    -- определяем наш класс, который на самом деле объект
    local HelloClass = object:extend(function(class)
      -- конструктор (необязательно)
      function class:init(name)
        self.name = name
      end
    
      -- метод класса
      function class:sayHello()
        print("Hello " .. self.name)
      end
    end)
    
    local hello = HelloClass:new("John")
    hello:sayHello()
    


    Как видим, вся магия заключается в методе extend(traits..., f), который расширяет текущий объект.

    Можно определять переменные внутри

    local object = require("object")
    local Status = object:extend(function(status)
      status.HTTP_200_OK = {200, "OK"}
      status.HTTP_405_METHOD_NOT_ALLOWED = {404, "Method not allowed"}
    end)
    
    print(Status.HTTP_200_OK[2])
    


    Статические методы

    Куда же без них…

    local object = require("object")
    local MathUtils = object:extend(function(class)
     function class.square(x)
      return x * x
     end
    end)
    
    -- вызываем статический метод
    print(MathUtils.square(10))
    
    -- вызывает тот же метод но уже через инстанс
    print(MathUtils:new().square(10)) -- 100
    


    Конструктор

    При создании нового экземпляра через new() вызывается конструктор init()

    local Counter = object:extend(function(class)
       -- конструктор, который принимает какой-то параметр (может быть много параметров)
       function class:init(initial)
         self.ticks = initial or 0
       end
    
       function class:tick()
         self.ticks = self.ticks + 1
       end
    
       function class:getTicks()
         return self.ticks
       end
    end)
    
    local c = Counter:new()
    c.tick()
    c.tick()
    print(c:getTicks() == 2)
    


    Наследование и перегрузка методов

    Как я упомянул, наследование сделано как single inheritance, то есть отнаследоваться можно только от одного «класса», однако есть еще трейты, о которых поговорим чуть позже. Перегрузка методов не вызывает никаких вопросов.

    local Shape = object:extend(function(class)
      function class:getArea()
        return 0
      end
    end)
    
    local Square = Shape:extend(function(class)
      function class:init(side)
        self.side = side
      end
    
     -- перегружаем метод
      function class:getArea()
        return self.side * self.side
      end
    end)
    
    local sq = Square:new(10)
    print("Area = " .. sq:getArea())
    


    Вызов родительского метода

    Для этого надо использовать второй параметр лямбда-функции, которую передаете в extend, которая есть ссылка на родительский объект (который хотим расширить)

    local Foo = object:extend(function(class)
      function class:init(value)
        self.value = value
      end
    
      function class:say()
        print("Hello " .. self.value)
      end
    end)
    
    class Bar = Foo:extend(function(class, parent)
      function class:init(value)
        -- вызывает конструктор родителя
        parent.init(self, value)
       end
    end)
    
    local foo = Foo:new("World")
    foo:say() -- напечатает "Hello World"
    
    local bar = Bar:new("World")
    bar:say() -- напечатает "Hello World"
    


    Трейты

    Когда не хватает множественного наследования как в С++, можете воспользоваться трейтами, которые расширяют функционал.

    Просто передайте ваши трейты в начало аргументов extend()

    local TraitX = function(trait)
      function trait:setX(x)
        self.x = x
        return self
      end
      function trait:getX()
        return self.x
      end
    end
    
    local A = object:extend(TraitX, function(class)
      function class:say()
        print(self.x)
      end
    end)
    
    A:new():setX(10):say()
    


    Полезные функции

    is_instanceof(self, instance) — вернет true, если instance является прямым или непрямым наследником self

    local ClassA = object:extend()
    local ClassB = object:extend()
    
    local obj_a = ClassA:new()
    local obj_b = ClassB:new()
    
    print(obj_a:is_instanceof(ClassA)) -- true
    print(obj_a:is_instanceof(object)) -- true
    print(obj_a:is_instanceof(ClassB)) -- false
    


    is_typeof(self, instance) — вернет true, если instance является прямым наследником self

    local ClassA = object:extend()
    local ClassB = object:extend()
    
    local obj_a = ClassA:new()
    local obj_b = ClassB:new()
    
    print(obj_b:is_typeof(ClassA)) -- false
    print(obj_b:is_typeof(ClassB)) -- true
    


    LuaJIT

    Как альтернатива, поддерживается работа в LuaJIT.

    Где код, Карл?

    Здесь
    • +10
    • 9,1k
    • 4
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 4
    • +8
      Программист на Фортране может писать на Фортране на любом языке программирования :)
      • –2
        Это стопицотая статья про то как сделать ООП на lua,
        Карл!
        • 0
          Ну вы хоть напишите какая версия lua поддерживается. 5.1?5.2?5.3 only? luajit — ок, а kahlua, а luaj?
          • 0
            Вот одна из наиболее удачных, на мой взгляд, реализаций ООП на Lua.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.