Lua如何实现OO?

问题描述:

Lua没有支持面向对象的构建,但它允许您自己构建它。你能分享一些可以实现面向对象的方法吗?Lua如何实现OO?

请为每个答案写一个例子。如果你有更多的例子,请发表另一个答案。

+1

我将增加奖励它,一旦我弄清楚如何,在让我们说“大约一个星期的”得到这么顶端回答: ) – 2011-01-25 21:37:21

+1

你的意思是像C++?那么,最好不要进入这场辩论:( – 2011-01-25 21:37:51

+0

Lua的PIL有详细的参考资料http://www.lua.org/pil/16.html – 2011-02-05 15:47:23

我喜欢把OOP看作是一个容器内的数据封装(对象),再加上可以用这些数据完成的一部分操作。还有更多,但是让我们假设这个简单的定义是全部的,并且从Lua中构建了一些东西(对其他OO实现的一些熟悉也可以对读者有很大的帮助)。

由于任何人只要有一点点暴露在Lua中可以知道,表是存储键值对,并用绳子组合整洁的方式,事情开始变得很有趣:

local obj = {} -- a new table 
obj["name"] = "John" 
obj["age"] = 20 
-- but there's a shortcut! 
print("A person: " .. obj.name .. " of the age " .. obj.age) 

字符串值因为表中的键可以以非常相似的方式访问C中的结构成员或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() 

太棒了吧?我们现在有了将函数存储在表格中的方法,并且您可以再次看到它类似于其他OOP语言中方法的使用方式。但缺少一些东西。我们如何在我们的方法定义中访问属于我们对象的数据?这通常是通过改变功能的签名在表中是这样解决的:

-- 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如何工作。但是男孩,在我们的方法调用中明确地传递所有这些对象不是不方便吗?是啊,很困扰我太多,所以有另一种快捷方式,以帮助您在使用OOP的:

obj:hello() -- is the same as obj.hello(obj) 

最后,我刚刚触及到如何可以做到这一点的表面。如在Kevin Vermeer's comment中已经注意到的那样,Lua Users Wiki是关于该主题的极好信息来源,并且您可以了解如何实现在此答案中忽略的OOP的另一个重要方面(私有成员,如何构造对象,继承,...)。请记住,这种做事方式只是Lua哲学的一小部分,为您提供了构建更高级构造的简单正交工具。

对于一个快速和肮脏的OO实现我做这样的事情 -

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()); 

一个更全功能的方法,我会用一个面向对象的库作为被eemrevnivek提及。您还可以找到一个简单的课程功能here,它位于图书馆的全部和快速和肮脏之间。

我通常使用的方法是这样的:

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} 

那么,结论是:和元表和一些聪明的想法,关于什么是可能的:元表带班工作时是真正的魔法。

我看到的最佳解决方案并不是在Lua中实现面向对象,它不是自然的和不规则的,因此需要很多行;相反,使用luabridge或luabind在C++中实现它,它是自然而强大的!

它采用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) 

同样一个级别的传承支持...

这已经是回答,但无论如何,这是我的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's 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.