Привет,
я придумал свой велосипед для реализации прототипного подхода ООП в 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/
Добавить комментарий