在不明确为OO而生的语言里玩OO往往是一件非常有趣的事情,因为语言层本身不支持,所以为了达到OO的效果就必须要深入这门语言进行研究和探索,即所谓"深入语言编程"。
于是这次来看一看在LUA里怎么OO,方法取自《lua中文手册》Chapter 16,这里做个整理和记录,顺带写一些心得。
如何定义对象和类:
LUA是弱类型脚本语言,因此在LUA里类型感很缺失。因此并没有一个明确的"类"和"对象"的概念,这点和C++大不同。LUA里我们首先用Table实现一个对象,该对象作为"原型对象"充当类的职能。通过传递一个新的table给原型对象的特定方法,将该Table构造为一个和原型对象一样的新对象实例(跟构造函数一个意思),是LUA里常见的OO技术之一,代码类似这样:
<!- Code Begin>
do
--成员函数定义
local function _set_age(self, value)
self.age = value
end
--成员函数定义
local function _print_age(self)
print(string.format("age = %d", self.age)
end
--原型对象中的对象生成函数
local function _new(self, newobj, agevalue)
newobj = newobj or {}
self.__index = self
setmetatable(newobj, self)
newobj.age = agevalue;
return newobj
end
--定义原型对象,以及该对象需要公开的所有方法
human = {
age = 27,
set_age = _set_age,
print_age = _print_agem
new = _new
}
end
-- 使用原型对象,进行对象实例化
Boy1 = human:new(Boy1, 10)
Boy2 = human:new(Boy2, 5)
-- 通过下面的语句可以看出来,Boy2和 Boy1是两个完全独立的对象,拥有自己的数据成员。但是这是为什么?
Boy1:print_age()
Boy1:set_age(20)
Boy1:print_age();
Boy2:print_age();
<!-Code End>
关于上面问题的解释:
这就是所谓"深入语言编程"和"使用语言编程的区别"。还是来看human:_new()函数,self.__index = self,使得调用时human.__index = human,这样human本身就成为了一个metatable,随后的setmetatable(newobj,self) 调用,为newobj设置metatable为human,这里就是关键点。newobj的metatable.__index = human,就意味着当从newobj中读取任何数据时,如果不存在则转入human中继续读取(human也是一个Table),但是如果你为存在于human中的一个属性赋值,则因为lua是弱语言,那么在newobj中会直接添加和这个属性同名的成员,这样下次你再读取该属性时,直接就从newobj中取值了(因为你刚才的赋值操作为newobj添加了这个变量成员),这样一来,newobj也就拥有了属于自己的成员变量(推广到每一个调用human:new()产生的对象身上,就知道这些对象是原型对象的相互独立实例了)。
这里利用的lua特性有2个:1.metatable的__index操作符 2.lua作为弱语言,不要求先声明后使用。 至于 self.__index = self,一方面是一种优化,令原型对象本身直接作为metatable,节省一个table的开销,更重要的是为OO的另一个特性"继承"提供了基础。关于继承下次再说。现在下班走人吃饭去% :-)