Lua 基础之面向对象编程
来源:互联网 发布:淘宝图防盗图怎么设置 编辑:程序博客网 时间:2024/05/18 18:42
对象
lua 中的 table 其实就是对象,理由如下
1. table 与对象一样可以拥有状态
2. table 与对象一样拥有一个独立于其值的标识 self,值相同的两个 table 是两个不同的对象
3. table 与对象一样拥有独立于创建者和创建地的生命周期
方法
table 中字段的值可以是任意类型,如果某个字段的值是一个函数,那这个函数就称为对象的方法
Account = { balance = 100, withdraw = function(v) Account.balance = Account.balance - v end}
withdraw 是全局对象 Account 的一个方法。对象具有独立的生命周期,因此对象的方法也只有这个对象本身能访问,比如下面的操作是错误的
a = AccountAccount = nila.withdraw(10)
在上面的代码,我们将对象 Account 改名为 a,然后通过 a 去调用 withdraw 方法,执行的时候会发现 Account 对象不存在,当然 Account.balance 也会找不到
self 参数
对象中的方法要访问对象的其它字段,需要显式地在方法中使用全局对象名,这种编程习惯是非常不好的,这种方式无法从一个对象拓展出其它对象,更无法实现类的概念,因为只能使用创建对象时唯一的全局对象名,比如上面的 Account,任何操作都只能使用 Account 这个名称
这个问题的解决方案是给每一个方法指定其操作的”接收者“,通常使用 self 或 this 来表示这个”接收者“,在 lua 中使用 self 参数
Account = {balance = 100}Account.withdraw = function(self, v) self.balance = self.balance - venda = AccountAccount = nila.withdraw(a, 1)print(a.balance)
方法定义时指定第一个参数为 self,然后在调用方法时把具体的对象传进去。一种更简单写法是使用冒号,使用这种方式可以隐藏 self 参数
function Account:withdraw(v) self.balance = self.balance - venda:withdraw(1)
类
实际上 lua 并没有类的概念,但我们可以使用”原型“来实现类与对象之间的关系。把一个表设为另一个表的原型(metatable),那这个原型表就相当于是一个类,而继承自这原型表的表就是对象
lua 中的类就是对象元表来实现的,通过设置元表的 __index 元方法,当访问一个对象不存在的字段或方法时,可以通过 __index 元方法在类中查找
-- 定义类Account = {balance = 0}-- 构造函数function Account:new(o) o = o or {} setmetatable(o, self) self.__index = self return oendfunction Account:withdraw(v) self.balance = self.balance + vendfunction Account:deposit(v) self.balance = self.balance - vend-- 创建对象a = Account:new()a:withdraw(100)a:deposit(50)print(a.balance)
分析上面代码的执行过程:
* Account:new() 会创建一个 table(对象),这个 table 的 metatable 是 Account
* a 没有 withdraw 方法,调用 a:withdraw(100) 时会到 Account 中查找 withdraw 方法,找到之后执行 self.balance = self.balance + v,此时 self 的值是 a,a 没有 balance 字段,因此同样的会到 Account 中查找;赋完值之后 a 就已经从 Account 继承了 balance 字段,下次访问就不需要到 Account 中查找了
* 调用 a:deposit(50) 同样会到 Account 中查找,因为 a 并没有 deposit 方法;但 a 已经有 balance 字段了,因为 a.balance = a.balance - v 可直接执行
继承
其实实现类的过程就是一种继承的行为,一个对象继承自另一个对象,被继承的对象就可以看作是一个类。我们可以继承一个类后重写它的一些方法生成一个子类,再继承这个子类来创建对象。执行对象的方法时会自下往上查找这个方法,如果在本对象找不到方法则向它的类原型查找,如果还没找到,则向父类查找,一直找到最原始的父类。
-- 定义父类Account = {balance = 0}function Account:new(o) o = o or {} setmetatable(o, self) self.__index = self return oendfunction Account:withdraw(v) if self.balance < v then error("你的钱不够了!", 2) else self.balance = self.balance - v endendfunction Account:deposit(v) self.balance = self.balance + vend-- 继承SpecialAccount = Account:new()-- 重写方法function SpecialAccount:withdraw(v) if self.balance < v then if self:getLimit() < v - self.balance then error("你不能透支这么多钱!", 2) else self.balance = self.balance - v end else self.balance = self.balance - v endend-- 子类的新方法function SpecialAccount:getLimit() return self.limit or 0end-- 创建子类对象a = SpecialAccount:new({limit = 100})a:withdraw(90)-- a:withdraw(20) -- errorprint(a.balance)
多重继承
lua 中的继承不是原生,实现的关键是元表的 __index 元方法,单一继承的方式是设置 __index 为元表本身,这样在子类找不到的字段和方法就会到父类,也就是子类的元表去找。如果 __index 不设置元表本身,而是一个函数,通过这个函数遍历它子类的父类,再找到对应的字段或方法,这样就可以实现多继承了
可以使用工厂模式来动态创建新类
-- 新类工厂CreateClass = function(...) -- 定义新类 local class = {} -- 定义父类集合 local parent = {...} -- 设置新类的元表 setmetatable( class, {__index = function(t, k) for _, v in ipairs(parent) do if v[k] then return v[k] end end end} ) -- 新类的元方法为自身 class.__index = class -- 定义新类构造函数 class.ctor = function(self, o) o = o or {} -- 设置对象的元表为新类 setmetatable(o, class) return o end return classend-- 使用工厂生产新类NamedAccount = CreateClass(Account, Name)-- 创建新类实例ac = NamedAccount:ctor()ac:set_name("myAccount")print(ac:get_name())ac:withdraw(100)print(ac:get_balance())
- 在工厂方法里面,创建一个新类,然后指定新类的元表,元表的 __index 值不是具体的某个父类,而是一个新方法,准确来说是一个闭包,还包括非局部变量 parent 通过这个方法可以遍历所有的父类,找到要找的字段或方法。
- 这样一个拥有多个父类的新类就定义出来了,再给这个新类定义一个构造方法,设置其实例的元表为这个新类,而这个元表的 __index 值也是这个新类本身,这是单一继承的东西
- 调用这个工厂方法,把所有父类通过参数传进来,就生成了一个拥有多个父类的新类
- 调用新类的构造方法,实例化新类的一个对象,这个对象的元表是这个新类,而新类的元表是自定义的,其 __index 元方法可以遍历所有父类
- 在使用的时候,首先通过实例对象去调用某个方法,比如 set_name 方法,发现这个对象没有这个方法,然后就通过它的元表向它的父类查找。它的元表是 NamedAccount,NamedAccount 的 __index 是 NamedAccount 本身,所以解释器会查找 NamedAccount,发现也没有找到 set_name 这个方法,于是继续通过元表向父类查找。NamedAccount 元表的 __index 元方法遍历 NamedAccount 所有的父类,发现 Named 类中有 set_name 这个方法,于是查找结束,调用
Name:set_name(v)
单一方法
使用一个方法来表示一个对象,这种方式其实是用闭包 colsure 来表示对象。单一方法实现的对象虽然无法继承,但具有完全的私密性,比如实现一个调度器
newObject = function(value) return function(action, v) if action == "get" then return value elseif action == "set" then value = v else error("invalid action") end endendd = newObject(10)print(d("get"))d("set", 100)print(d("get"))
私密性
lua 本身没有提供私密性的机制,但我们可以利用 lua 的灵活性来模拟类的私密性。我们在创建对象的时候不返回对象本身,而是返回所有外部接口的集合,通过这些外部接口就可以访问和操作这个对象。
Account = function(balance) local self = {_balance = balance} local _withdraw = function(v) self._balance = self._balance - v end local _deposit = function(v) self._balance = self._balance + v end local _get_balance = function() return self._balance end return {withdraw = _withdraw, deposit = _deposit, get_balance = _get_balance}enda = Account(0)a.withdraw(100)print(a.get_balance())a.deposit(10)print(a.get_balance())
总结
lua 中类的概念主要通过继承来实现的,实现继承主要有以下两步
1. 设置元表 setmetatable(child.parent)
2. 设置 __index 元方法 parent.__index=parent
- Lua 基础之面向对象编程
- Lua基础之面向对象
- Lua学习之6:面向对象编程
- lua 面向对象编程
- lua 面向对象编程
- Lua面向对象编程
- Lua面向对象编程
- Cocos2d-x Lua游戏开发之Lua 面向对象编程
- 【Lua】Lua之面向对象
- Lua中函数与面向对象编程的基础
- Lua中的面向对象编程
- lua中的面向对象编程
- Lua中的面向对象编程
- Lua中的面向对象编程
- python之面向对象编程基础
- Python基础之五面向对象编程
- Python 之 基础面向对象编程
- Lua 中的面向对象编程之封装和继承
- 在windows平台下MySql启动时的1067错误的解决方法及反思
- 595. Big Countries
- Hadoop之Combiner与自定义Combiner(笔记8)
- 260. Single Number III----leetcode
- 算法题目---字符串的排列
- Lua 基础之面向对象编程
- QT用Cmake转VS之后编译出现缺少头文件。。
- (转载)Java泛型
- vs2013MFC中静态文本框中的鼠标响应事件
- HDU4857 逃生(拓扑排序)
- 欢迎使用CSDN-markdown编辑器
- Spring Boot干货系列:(十)开发常用的热部署方式汇总
- C++ 模板基础(一)
- 数组、二维数组与指针