Lua(5.2版本)中元表在面向对象编程中的作用是什么?

4

我很难理解元表(metatables)是如何工作的,以及为什么在Lua中创建类和实现继承时需要元表。我发现每个关于Lua面向对象编程(OOP)的示例都有所不同,但它们始终使用元表,特别是__index属性。以下是我实现简单继承的方式:

Animal = {}

function Animal.New()
  local self = {}
  self.sName = "Generic Animal"

  self.GetName = function(self)
    return self.sName
  end

  self.Speak = function(self)
    -- Do nothing, abstract function
  end

  return self
end

Dog = {}

function Dog.New()
  local self = Animal.New()
  self.sName = "Dog"

  self.Speak = function(self)
    print("Bark!")
  end

  return self
end

Labrador = {}

function Labrador.New()
  local self = Dog.New()
  self.sName = "Labrador"
  return self
end

Chihuahua = {}

function Chihuahua.New()
  local self = Dog.New()
  self.sName = "Chihuahua"

  self.Speak = function(self)
    print("Yap yap!")
  end

  return self
end

-- Test --

l = Labrador.New()
print(l:GetName())
l:Speak()

c = Chihuahua.New()
print(c:GetName())
c:Speak()

d = Dog.New()
print(d:GetName())
d:Speak()

a = Animal.New()
print(a:GetName())
a:Speak()

输出:

Labrador
Bark!
Chihuahua
Yap yap!
Dog
Bark!
Generic Animal

就我所看到的,这很好用。使用元表会如何改善我的设计呢?

1
你读过《Lua编程》的第13章和第16章吗?我认为它们提供了最好的解释。在线版本是针对5.0的,但我认为基础知识仍然相同。http://www.lua.org/pil/13.html - Jane T
我在谷歌搜索中没有遇到过这个特定的第13章。它对于更好地理解元表确实非常有帮助。谢谢。 - Joe S
1个回答

1
没有人说元表是面向对象编程所必需的。它们是有用的,但不是必要的。
元表允许您隐藏数据。我可以很容易地破坏您所有仔细编写的代码:
local dog = Chihuahua.New()
dog.sName = nil --Oops.
dog.GetName = nil --Oops.

它还允许其他可疑的构造,例如在对象中存储其他数据:dog.newVar = foo
面向对象编程不仅仅是继承。良好的面向对象编程还应该包括封装,以防止对象因意外误用而受损。元表可以通过使用空表作为主表,并通过__index__newindex元方法过滤所有设置和获取来实现这一点。这样,只有对象可以将数据存储在实际表中。除非您提供了明确的访问权限,否则只有对象才能检索它。

嗯,我明白你在说封装的问题,但实际上在Lua中,即使有人尽最大努力进行封装,也很难阻止其他程序员搞砸事情,对吧?无论你如何封装,它仍然可能像“Chihuahua = nil”一样容易出错。此外,我还没有测试过,但是即使将函数定义与元表连接起来,你仍然可以搞砸它们,对吧?换句话说,“Chihuahua:GetName”是否仍然可以被设置为nil,无论它是否是元表的一部分? - Joe S
@JoeS:胡说八道;Lua有办法防止甚至Chihuahua = nil起作用。你所要做的就是锁定脚本的环境。而Lua有很多种方法可以做到这一点。是的,如果用户决心,那么他们可以通过获取元表来破坏对象的封装(除非您删除getmetatable函数。然后他们就完了)。但重点不是防止恶意使用,而是愚蠢。简而言之,你完全可以做到这一点。 - Nicol Bolas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接