面向对象编程
对象的实现
在lua中table就是一种对象
1.有本身的状态
2.有本身的惟一标识self
3.有本身的生命周期
使用table能够本身实现面向对象的几乎全部特性
把函数定义在table中,并使用t.func的形式访问,如同方法调用
Account = {balance=0}
function Account.withdraw(v)
Account.balance = Account.ballance - v
end
但在函数中使用全局的Account是一个很差的习惯
在lua中使用面向对象方式编程时尽可能使用self和t:func的形式
带有标识自身对象的方法定义:
function Account.withdraw(self, v)
同上的语法糖定义:
function Account:withdraw(v)
带有标识自身对象的方法调用:
a1.withdraw(a1, v)
同上的语法糖定义:
a1:withdraw(v)
使用":"会把自身当作每个参数隐式的传入
使用self是面向对象编程的一大核心,不少语言为程序员隐藏了这个参数
self在C++中就至关于this指针
类的实现
在lua中没有类的概念,但能够本身来实现
function Account:new(o)
o = o or {} --若是用户没有提供table,则建立一个
setmetatable(o, self)
self.__index = self
return o
end
当使用new函数来建立对象后(其实就是建立一个新的table),全部的访问都会从Account这个table里找
这种状况就至关于Account是一个类,也能够说是一个原型模具,全部新建立的table都拥有他的属性和方法
a = Account:new{balance=0}
a:deposit(100.00)
因为deposit在新建立的table a里没有定义
所以经过它的元表__index来查找,a的元表是Account,
所以会调用Account的deposit方法,但self传入的是a
这就实现了a继承了Account的方法deposit
在这里也看到了使用self来标识调用对象的好处
继承和派生
sa = Account:new()
s = sa:new{limit=1000.00}
第一行sa继承了Account,sa的元表是Account,找不到的方法就去Account里去找
第二行s继承了sa,这里的new是Account的方法但传入的self是sa,
导致s的元表是sa而sa的元表又是Account
因此一层一层的继承了下去,而且在每一层的派生table里均可以定义重载方法和新的方法
在lua里的能够实现多重继承,就是使元表的__index指向一个函数,而后自行判断并处理
私密性
使用table来实现面向对象的编程方式,几乎能够实现全部面向对象的编程特性
但它没有也不想去实现的就是对象的私密性,也就是c++里的private、public、protected
这与lua设计的初衷有关,lua定位于小型的程序开发,参与一个工程的人不会不少,自行约束
非要实现私密性的话lua也不是不能,只是不能再使用table和元表的方式了
可使用函数闭包来实现私密性:
function newAccount(init)
local self = {blance=init}
local withdraw = function(v)
self.balance = self.balance - v
end
local deposit = function(v)
self.balance = self.balance + v
end
return{withdraw = withdraw, deposit = deposit}
end
在闭包里定义一个table的upvalue,而后把全部闭包函数都定义在这里table里,
而后返回这个table,用key访问内部方法
使用闭包实现对象的方式比用table效率高并实现了绝对的私密性,但没法实现继承,至关于简单的小对象
甚至能够在闭包里仅定义一个方法,而后经过key来判断调用是什么方法
Tcl/Tk对它的窗口部件就使用这种方法