Lua学习之旅-面向对象编程

面向对象

Lua中的table就是一种对象,这句话可以从3个方面来证实。
首先,table于对象一样可以拥有状态。
其次,table也与对象一样拥有一个独立于其值的标识(一个self)。例如,两个具有相同值的对象是两个不同的对象。
最后,table与对象一样具有独立于创建者和创建地的生命周期。
对象有其自己的操作,table同样也有:

Account = { balancce = 0 }
function Account.withdraw(v)
		Account.balance = Account.balance - v
end

上面的代码创建了一个新的函数,并将该函数存入到了Account的withdraw字段中。如需调用可以这样:

Account.withdraw(100.00)

但这种函数中使用的是全局名称“Account”是一个不好的编程习惯。因为这个函数只能针对特定对象工作,并且这个特定对象必须存储在特定的全局变量中。如果改变了全局变量的名称,withdraw就再也不能工作了:

a = Account;
Account = nil
a.withdraw(100.00) --- error

这种行为违反了对象的特性,即对象拥有独立的生命周期。
有一种灵活的方法,即指定一项操作作用的“接受者”,因此需要一个额外的参数来表示该接受者。这个参数通常称为self或this:

function Account.withdraw(self,v)
		self.balance = self.balance - v
end


a1 = Accont;
a2 = {balance = 0, withdraw = Account.withdraw}
Account = nil
a1.withdraw(a1,100.00) -- OK
a2.withdraw(a2, 260.00) -- OK

**使用self参数是所有面向对象语言的一个核心。**大多数面向对象语言都能对程序员隐藏部分self 参数,从而使得不必再显示的声明这个参数。
Lua只需要使用冒号,则能隐藏该参数。

function Account:withdraw(v)
		self.balance = self.balance - v
end

冒号的作用是在一个方法定义中添加一个额外的隐藏参数,以及在一个方法调用中添加一个额外的实参。
冒号只是一种语法的便利(语法糖),并没有引入什么新的东西。

1.1 类(实例化)

Lua中没有实际的类的概念,每个对象只能自定义行为和形态。不过在Lua可以模拟类。
在Lua中模拟类也不困难,可以参照一些基于原型的语言,例如Self和NewtonScript。在这些语言中对象是没有“类型”的,而是每个对象
在Lua中实现原型很简单,使用继承即可。更准确的说,如果有2个对象a和b,要让b作为a的一个原型,只需要:

setmetatable(a,{__index = b})

在此之后,a就会在b中查找它没有的操作。若将b称为是对象a的类,只不过是术语上的一个变化。
回到先前的银行账户实例。为了创建更多与Account行为类似的账号,可以让这些对象从Account行为中继承这些操作。具体做法就是使用__index元方法。

function Account:new()
		local o = o or {}
		setmetatable(o,self)
		self.__index = self
		return o
end
--当调用Account:new时,self就等于Account。因此此处Account来代替self也没问题。不过在引入类继承时,使用self则更为准确。
a = Account:new{balance = 0}
a:deposit(100.00)

1.2 继承

由于类也是对象,它们也可以从其他类中获得方法。这种行为就是一种继承。

Account = {balance = 0}
function Accpunt:new (o)
		o = o or {}
		setmetatable(o,self)
		self.__index = self
		return o
end

function Account:deposit(v)
		self.balance = self.balance  + v
end

function Account:withdraw(v)
		if self.balance < v then error("球钱没得 还想超?gun") end
		self.balance = self.balance  - v
end
-----------------------以下是派生类 start
SpecialAccount = Account:new(limit = 100)
--这里创建派生类的时候 并且为派生类声明了一个新的变量 limit 并且赋值
function SpecialAccount:withdraw(v)
		if v - self.balance >= self:getLimit() then
				error("你贷款最多贷".. self:getLimit())
		end
		self.balance = self.balance - v
end

function SpecialAccount:getLimit()
		return self.limit or 0
end
----------------------- end
----------------------- 创建实例
local user = SpecialAccount:new()
--创建实例user时 SpecialAccount中没有new,查找元表Account,__index字段中的new,此时就是Account:new()
user.deposit(5000.00)
--这里执行时,其实是执行的Account的deposit
user.withdraw(5099.00)
--这里是执行的SpecialAccount的withdraw

以上就是完成一个类的继承。

1.3 多重继承

多重继承的方法在于用一个函数作为__index元字段。例如在一个table的元表中,__index字段为一个函数。那么在原table中找不到一个key字段,就会调用 这个函数。基于这点就可以让__index在其他地方查找这个key。
多重继承意味着一个类可以继承多个基类。因此不能使用一个类中的方法来创建子类,而是要定义一个特殊的函数来创建。在这个函数中会创建一个新的table表示新类,其中一个参数表示新类的所有基类,创建时它会设置元表中的__index元方法,而多重继承正是在这个元方法中实现的。

-- 辅助查找函数 遍历列表来找到对应的Key
local function search(k,plist)
 		for i = 1 ,#plist do
 				local v = plist[i][k]
 				if v then return v end
 		end
end

fucntion createClass(...)
	local c = {}
	local parents = {...}
	setmetatable(c,{__index = function(t,k)
		 return search(k,parents)
	end})
	c.__index = c
	
	function c:new(o)
			o = o or {}
			setmetatable(o,c)
			return o
	end
	return c  -- 返回新类
end
---------------------------------以下来实现多继承
Named = {}
function Named:getName()
		return self.name
end

function Named:setname(n)
		self.name = n
end

NameAccount = createClass(Account,Named)
account = NameAccount:new({name = "Paul" })
---这里调用new的时候 会先从NameAccount这里面去找然后,查找它的元表c的__index,发现没有,然后再去找c的元表的__index是一个方法,方法中调用了search,发现Account中有一个new,就实现了初始化。

print(account.getName()) --- Paul
--这里调用 同上

以上就是一个多重继承的实现方式了。但是这种方式性能不如单一继承,由此做了一个改进:

setmetatable(c,{__index = function(t,k)
		local v = search(k,parents)
		t[k] = v ---这里把它保存下来 以便下次访问
		return v
end})

1.4 私密性

Lua在设计对象时,没有提供私密性机制。不过可以实现:
通过2个table来表示一个对象。一个table用来保存对象的状态;另一个用与对象的操作。对象本身是通过第二个table来访问的(这里可以理解为C#的属性,不过get是一个单独的表)。

function newAccount (initBalance)
	local self = {balance = initBalance} --这里使用了闭包方式 将balance 的访问权限 固定在了 newAccount的table self中 因为外部访问不到 self 
	local withdraw = function (v)
 			self.balance = self.balance - v
 	end
 	local deposit = function (v)
 			self.balance = self.balance + v
 	end
 	local getBalance = function () return self.balance end
 	return {
 			withdraw = withdraw,
 			deposit = deposit,
 			getBalance = getBalance
 		}
end

这种设计给予存储在self table中所有的东西完全私密性。当newAccount返回后,就无法直接访问这个table了。只能通过newAccount创建的函数来访问它。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值