Как можно реализовать OO в Lua?
У Lua нет встроенной поддержки OO, но она позволяет вам создавать ее самостоятельно. Не могли бы вы поделиться некоторыми из способов реализации OO?
Пожалуйста, напишите один пример за каждый ответ. Если у вас есть другие примеры, отправьте еще один ответ.
Ответы
Ответ 1
Мне нравится думать, что ООП является инкапсулированием данных внутри контейнера (Объект) в сочетании с подмножеством операций, которые могут выполняться с этими данными. Это намного больше, но предположим, что это простое определение - это все и что-то построить в Lua (также некоторое знакомство с другими реализациями OO может быть хорошим стимулом для читателя).
Как может знать кто-нибудь, у кого мало внимания Lua, таблицы - это аккуратный способ хранения пар ключ-значение и в сочетании со строками, вещи начинают становиться очень интересными:
local obj = {} -- a new table
obj["name"] = "John"
obj["age"] = 20
-- but there a shortcut!
print("A person: " .. obj.name .. " of the age " .. obj.age)
Значения строк в виде ключей в таблице могут быть доступны так же, как и для членов структуры в C или публичных членов объекта на С++/Java и подобных языках.
И теперь для крутой волшебной трюки: пусть объединит это с анонимными функциями.
-- assume the obj from last example
obj.hello = function ()
print("Hello!")
end
obj.goodbye = function ()
print("I must be going.")
end
obj.hello()
obj.goodbye()
Удивительное право? Теперь у нас есть способы иметь функции, хранящиеся внутри наших таблиц, и снова вы видите, что это похоже на то, как методы используются на других языках ООП. Но чего-то не хватает. Как мы можем получить доступ к данным, принадлежащим нашему объекту, в наших определениях методов? Это обычно решается путем изменения сигнатуры функций в таблице примерно так:
-- assume the obj from last example
obj.inspect = function (self)
print("A person: " .. self.name .. " of the age " .. self.age)
end
obj.hello = function (self)
print(self.name .. ": Hello! I'm " .. self.name)
end
obj.goodbye = function (self)
print(self.name .. ": I must be going.")
end
-- now it receives the calling object as the first parameter
obj.inspect(obj) -- A person: John of age 20
obj.hello(obj) -- John: Hello! I'm John
obj.goodbye(obj) -- John: I must be going
Это решает это простым способом. Возможно, параллельный подход к тому, как все работает в Python (методы всегда получают явное я), может помочь вам узнать, как это работает в Lua. Но мальчик, неужели неудобно передавать все эти объекты явно в наших вызовах метода? Да, это беспокоит меня тоже, поэтому есть еще один ярлык, который поможет вам в использовании ООП:
obj:hello() -- is the same as obj.hello(obj)
Наконец, я только что поцарапал поверхность, как это можно сделать. Как было отмечено в комментарии Кевина Вермеера, Lua Users Wiki является отличным источник информации по этой теме, и там вы можете узнать все о том, как реализовать другие важные аспекты ООП, которые в этом ответе игнорировались (частные члены, как создавать объекты, наследование,...). Имейте в виду, что этот способ делать вещи - это небольшая часть философии Lua, предоставляя вам простые ортогональные инструменты, способные создавать более продвинутые конструкции.
Ответ 2
Для быстрой и грязной реализации я делаю что-то вроде:
function newRGB(r,g,b)
return {
red=r;
green=g;
blue=b;
name='';
setName = function(self,name)
self.name=name;
end;
getName = function(self)
return self.name;
end;
tostring = function(self)
return self.name..' = {'..self.red..','..self.green..','..self.blue..'}'
end
}
end
который затем можно использовать как -
blue = newRGB(0,0,255);
blue:setName('blue');
yellow = newRGB(255,255,0);
yellow:setName('yellow');
print(yellow:tostring());
print(blue:tostring());
для более полнофункционального подхода я бы использовал библиотеку oo, как упоминалось eemrevnivek. Вы также можете найти простую функцию класса здесь, которая находится где-то между полной библиотекой и быстрой и грязной.
Ответ 3
Это уже ответ, но в любом случае, здесь моя реализация oop: middleclass.
Это lib обеспечивает минимальный минимум для создания классов, экземпляров, наследования, полиморфизма и (примитивных) mixins с приемлемой производительностью.
Пример:
local class = require 'middleclass'
local Person = class('Person')
function Person:initialize(name)
self.name = name
end
function Person:speak()
print('Hi, I am ' .. self.name ..'.')
end
local AgedPerson = class('AgedPerson', Person) -- or Person:subclass('AgedPerson')
AgedPerson.static.ADULT_AGE = 18 --this is a class variable
function AgedPerson:initialize(name, age)
Person.initialize(self, name) -- this calls the parent constructor (Person.initialize) on self
self.age = age
end
function AgedPerson:speak()
Person.speak(self) -- prints "Hi, I am xx."
if(self.age < AgedPerson.ADULT_AGE) then --accessing a class variable from an instance method
print('I am underaged.')
else
print('I am an adult.')
end
end
local p1 = AgedPerson:new('Billy the Kid', 13) -- this is equivalent to AgedPerson('Billy the Kid', 13) - the :new part is implicit
local p2 = AgedPerson:new('Luke Skywalker', 21)
p1:speak()
p2:speak()
Вывод:
Hi, I am Billy the Kid.
I am underaged.
Hi, I am Luke Skywalker.
I am an adult.
Ответ 4
Используемый подход обычно выполняется следующим образом:
class = {} -- Will remain empty as class
mt = {} -- Will contain everything the instances will contain _by default_
mt.new = function(self,foo)
local inst={}
if type(foo) == "table" then
for k,v in pairs(foo) do
inst[k]=v
end
else
inst.foo=foo
end
return setmetatable(inst,getmetatable(class))
end
mt.print = function(self)
print("My foo is ",self.foo)
end
mt.foo= 4 --standard foo
mt.__index=mt -- Look up all inexistent indices in the metatable
setmetatable(class,mt)
i1=class:new() -- use default foo
i1:print()
i2=class:new(42)
i2:print()
i3=class:new{foo=123,print=function(self) print("Fancy printing my foo:",self.foo) end}
Ну, вывод: с метатегами и некоторым умным мышлением, о чем-то возможно: metatables - это настоящая магия при работе с классами.
Ответ 5
Лучшим решением, которое я видел, является не реализация OO в Lua, где он не является естественным и неоднородным и, следовательно, занимает много строк; скорее, реализовать его на С++, используя luabridge или luabind, где это естественно и мощно!
Минималистский пример, который использует LuaBridge:
m.class_<MyClass>("MyClass")
.constructor<void (*) (/* parameter types */)>()
.method("method1", &MyClass::method1)
.property_rw("property2", &MyClass::getter2, &MyClass::setter2)
.property_ro("property3", &MyClass::property3)
Это приведет к естественному синтаксису lua:
c=MyClass()
c.method1()
c.property2 = c.property3 * 2
do_stuff(c.property3)
Также поддерживается одноуровневое наследование...