lua类的继承【单继承&&多继承】

一、单继承

---基类
---普通银行账户,只能取自己存的钱,相当于储蓄卡
Account = {
	id = -1,
	balance = 0
}


function Account:new(t)
	t = t or {}
	setmetatable(t, self)
	self.__index = self
	return t
end

function Account:deposite(x)
	self.balance = self.balance + x
end
	

function Account:withdraw(x)

	print("try to withdraw "..x)
	if x > self.balance then
		print("balance not enough!")
	else
		print("withdraw success")
		self.balance = self.balance - x
	end
end

function Account:check()
	print("your account has "..self.balance)
end

---派生类
--特殊银行账户,可以预支,相当于信用卡
SpecialAccount = Account:new()

function SpecialAccount:withdraw(x)
	print("try to withdraw "..x)
	if x > self.balance + self.limit then
		print("balance and limit left not enough!")
	elseif x <= self.balance then
		print("withdraw success from account ")
		self.balance = self.balance - x
	else
		print("withdraw part from limit ")
		self.limit = self.limit + self.balance - x
		self.balance = 0	
	end
	self:check()
end

--派生类实例
---Vip客户,
si = SpecialAccount:new({limit = 1000})
function si:check()
	print("------------------------")
	print("your account has "..self.balance)
	print("your limit has "..self.limit)
	print("------------------------")
end

示例调用如下:

s1 = Account:new()
s1:deposite(100)
s1:check()
s1:withdraw(120)
s1:withdraw(10)
s1:check()

SpecialAccount = Account:new()
SpecialAccount:deposite(100)

si = SpecialAccount:new({limit = 1000})
si:check()
si:withdraw(100)
si:withdraw(100)

    lua的派生类的实现与C++不同,我们不用特地写一个派生类的定义,而是直接从基类创建实例就可以得到派生类了,派生类之所以叫派生类,实际上只是定义了自己的方法而已。meatatable的机制让这种调用看上去像是“父类-子类”的模式。比如上面代码的SpecialAccount就是Account的一个实例而已。只不过这个实例定义了自己的方法withdraw,在调用SpeicalAccount时,如果找不到相关的变量就去Account里面找。最后的si既可以看作是SpecialAccount的实例也可以看做是它派生出来的一个新的类,可以有自己新的行为。

    值得注意的是如果单纯把SpecialAccount当做一个类,而不是当做一个实例来看的话,风险不大,因为我们可以要求用户创建实例的时候必须si = SpecialAccount:new({limit = 1000})带上limit字段;但是如果把SpecialAccount当做实例或者在创建新的实例的时候不带上limit字段,则可能在SpecialAccount:withdraw(x)函数中访问不存在的字段self.limit
lua类的继承【单继承&&多继承】_第1张图片

二、多继承

--个人信息类
local profile = {}
function profile:setName(name)
	self.name = name
end

function profile:getName()
	return self.name
end

--学校类
local School = {}
function School:setSchoolName(name)
	self.schooName = name
end

function School:getSchoolName(name)
	return self.schooName
end


--搜索基类函数的函数
local function search(k, plist)
--注意这里的plist不能用pairs遍历,因为它的最后一个数据健值是“n”,内容是数据,表示长度,而不是和前面的数据一样为【函数名 :函数地址】
	for i = 1, table.getn(plist) do
		local v = plist[i][k]
		if v~= nil then
			return v
		end
	end
	return
end

--创建一个有多基类的派生类,基类放在参数里面
function createClass(...)
	local t={}
	setmetatable(t, {__index = function(t, k) return search(k, arg) end})

	t.__index = t
	function t:new(o)
		o = o or {}
		setmetatable(o, t)
		return o
	end

	return t
end

---创建学生类
Student = createClass(profile, School)
stu = Student:new({name = "Doris"})
print(stu:getName())

stu:setSchoolName("Amoy")
print(stu:getSchoolName())

    因为多继承的类有多个基类,所以不能通过“创建基类的实例”的方式来创建派生类。在这里我们使用了一个创建类的函数createClass,它的参数是多个类,它的返回值就是派生出来的类。
    多继承实际上是通过去搜索基类的函数实现的,也就是通过metatable里面的__index去调用search函数实现。当调用stu:getName()的时候,发现stu表里面没有getName函数,则去调用了它的metatable里面的__index方法,__index方法调用了search(k, arg),这里的k是getName, arg是createClass(profile, School)里面的profileSchoolsearch函数去profileSchool两个表里面查找getName函数,找到就返回了。

    这种调用的弊端是每一次都要遍历所有的基类去查找函数,所以可以在第一查找到的时候就记录下来,创建自己的函数字段。如下

setmetatable(c, {__index = function (t, k)
        local v = search(k, arg)
        t[k] = v       -- save for next access
        return v
      end})

    另一个弊端是,如果多个基类里面的函数有同名的,那么通过这种查找方式可能得到错误的结果。【不过想来,应该也不会允许有同名函数的存在,因为table的健值是要求唯一的】
lua类的继承【单继承&&多继承】_第2张图片

你可能感兴趣的:(lua)