Прототипное ООП для 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.

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

Здесь

ссылка на оригинал статьи http://habrahabr.ru/post/259145/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *