...

суббота, 30 мая 2015 г.

Прототипное ООП для 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.
Где код, Карл?

Здесь

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

Комментариев нет:

Отправить комментарий