简体   繁体   English

如何在Lua中实现OO?

[英]How can one implement OO in Lua?

Lua does not have build in support for OO, but it allows you to build it yourself. Lua没有为OO构建支持,但它允许您自己构建它。 Could you please share some of the ways one can implement OO? 你能否分享一些可以实现OO的方法?

Please write one example per answer. 请为每个答案写一个例子。 If you have more examples, post another answer. 如果您有更多示例,请发布另一个答案。

I like to think of OOP as being the encapsulation of data inside a container (the Object) coupled with a subset of operations that can be done with this data. 我喜欢将OOP视为容器(Object)中的数据封装,以及可以使用此数据完成的操作子集。 There IS a lot more to it, but let's assume that this simple definition is all and build something in Lua from it (also some familiarity with other OO implementations can be a nice boost for the reader). 还有更多内容,但让我们假设这个简单的定义是全部并在Lua中构建一些东西(对其他OO实现的熟悉也可以为读者带来很好的推动)。

As anyone with a little exposure to Lua may know, tables are a neat way to store key-value pairs and in combination with strings, things start to become very interesting: 任何对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)

String values as keys in a table can be accessed in a way very alike to the members of a struct in C or the public members of an object in C++/Java and similar languages. 字符串值作为表中的键可以以与C语言中的结构成员非常相似的方式访问,或者以C ++ / Java和类似语言中的对象的公共成员访问。

And now for a cool magic trick: let's combine this with anonymous functions. 现在有一个很酷的魔术:让我们将它与匿名函数结合起来。

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

Awesome right? 真棒吗? We now have means of having functions stored inside our tables, and again you can see it resembles how methods are used in other OOP languages. 我们现在有了将函数存储在表中的方法,并且您再次可以看到它类似于在其他OOP语言中使用方法的方式。 But something is missing. 但缺少一些东西。 How can we access the data that belongs to our object inside our method definitions? 我们如何在方法定义中访问属于我们对象的数据? This is generally addressed by changing the signature of the functions in the table to something like this: 通常通过将表中函数的签名更改为以下内容来解决此问题:

-- 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

That solves it in a simple manner. 这以一种简单的方式解决了这个问题。 Maybe drawing a parallel to the way things work in Python (methods always get a explicit self) can aid you in learning how this works in Lua. 也许平行于Python中的工作方式(方法总是得到一个明确的自我)可以帮助你学习如何在Lua中工作。 But boy, isn't it inconvenient to be passing all these objects explicitly in our method calls? 但是男孩,在我们的方法调用中明确地传递所有这些对象不是很不方便吗? Yeah it bothers me too, so there's another shortcut to aid you in the use of OOP: 是的,它也困扰我,所以还有另一个快捷方式来帮助你使用OOP:

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

Finally, I have just scratched the surface of how this can be done. 最后,我刚刚谈到了如何做到这一点。 As has been noted in Kevin Vermeer's comment , the Lua Users Wiki is an excellent source of information about this topic and there you can learn all about how to implement another important aspects of OOP that have been neglected in this answer (private members, how to construct objects, inheritance, ...). 正如Kevin Vermeer的评论所指出的那样, Lua Users Wiki是关于这个主题的极好的信息来源,在那里你可以学到如何实现在这个答案中被忽略的OOP的另一个重要方面(私人成员,如何构造对象,继承,...)。 Have in mind that this way of doing things is a little part of the Lua philosophy, giving you simple orthogonal tools capable of building more advanced constructs. 请记住,这种做事方式是Lua哲学的一小部分,为您提供简单的正交工具,能够构建更高级的构造。

For a quick and dirty oo implementation I do something like - 对于快速而肮脏的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

which can then be used like - 然后可以使用像 -

blue = newRGB(0,0,255);
blue:setName('blue');

yellow = newRGB(255,255,0);
yellow:setName('yellow');

print(yellow:tostring());
print(blue:tostring());

for a more full featured approach I would use an oo library as was mentioned by eemrevnivek. 对于更全功能的方法,我会使用eemrevnivek提到的oo库。 You can also find a simple class function here which is somewhere between full on library and quick and dirty. 你也可以在这里找到一个简单的类函数它介于完全在库和快速和脏之间。

This is already answered, but anyway, here's my oop implementation: middleclass . 这已经回答了,但无论如何,这是我的oop实现: middleclass

That lib provides the bare minimum for creating classes, instances, inheritance, polymorphism and (primitive) mixins, with an acceptable performance. lib提供了创建类,实例,继承,多态和(原始)mixin的最低限度,具有可接受的性能。

Sample: 样品:

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

Output: 输出:

Hi, I am Billy the Kid.
I am underaged.
Hi, I am Luke Skywalker.
I am an adult.

The approach I use usually goes like this: 我使用的方法通常是这样的:

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}

Well, conclusion: with metatables and some clever thinking, about anything is possible: metatables are the REAL magic when working with classes. 好吧,结论:有了metatables和一些聪明的想法,关于任何事情都是可能的:metatables在使用类时是真正的魔力。

The best solution I saw is not to implement OO in Lua, where it is not natural and patchy, and hence takes many lines; 我看到的最好的解决方案是不在Lua中实现OO,它不是自然而且不完整的,因此需要很多行; rather, implement it in C++ using luabridge or luabind, where it is natural and powerful! 相反,使用luabridge或luabind在C ++中实现它,它是自然而强大的!

A minimalistic example which uses LuaBridge: 使用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)

This would translate into natural lua syntax: 这将转换为自然的lua语法:

c=MyClass()
c.method1()
c.property2 = c.property3 * 2
do_stuff(c.property3)

Also one-level inheritence is supported... 还支持一级继承...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM