如何在Lua中实现OO?

时间:2011-01-25 21:30:47

标签: oop lua

Lua没有为OO提供支持,但它允许您自己构建它。您能否分享一下可以实施OO的一些方法?

请为每个答案写一个例子。如果您有更多示例,请发布另一个答案。

5 个答案:

答案 0 :(得分:35)

我喜欢将OOP视为容器(Object)中的数据封装,以及可以使用此数据完成的操作子集。还有更多内容,但让我们假设这个简单的定义是完整的,并在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哲学的一小部分,为您提供简单的正交工具,能够构建更高级的构造。

答案 1 :(得分:11)

对于快速而肮脏的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提到的oo库。你还可以找到一个简单的类函数here,它介于库之间的完整和快速而又脏的之间。

答案 2 :(得分:7)

这已经回答了,但无论如何,这是我的oop实现:middleclass

lib提供了创建类,实例,继承,多态和(原始)mixin的最低限度,并且具有可接受的性能。

样品:

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.

答案 3 :(得分:2)

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

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和一些聪明的想法,关于任何事情都是可能的:metatables在使用类时是真正的魔力。

答案 4 :(得分:0)

我看到的最好的解决方案是不在Lua中实现OO,它不是自然而且不完整的,因此需要很多行;相反,使用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)

还支持单级继承...