Pull to refresh

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

Reading time 3 min
Views 12K
Привет, я придумал свой велосипед для реализации прототипного подхода ООП в 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.

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

Здесь
Tags:
Hubs:
+10
Comments 4
Comments Comments 4

Articles