博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《Programming in Lua 3》读书笔记(十二)
阅读量:5856 次
发布时间:2019-06-19

本文共 6422 字,大约阅读时间需要 21 分钟。

日期:2014.7.14     

PartⅡ Object-Oriented Programming
Lua中实现面向对象编程。
“如同OOP对象,table拥有状态;如同OOP对象,table拥有标识符---self,用来与其他变量做区分,而且两个table拥有同样的值也是不同的object(对象),因为self的不同;如同OOP对象,table也有生命周期,这个生命周期与谁在何处创建table是保持独立的”
对象是拥有自己的运算操作的,table也有,如
e.g.Account = {blanche = 0}function Account.withdraw(v)     Account.balance = Account.balance - vend
上述的函数就是OOP中称呼的method(方法)。当然,上述的使用技巧是不可取的:在函数体内使用全局变量Account。这样会造成严重的后果,而且这样使用限制性太大,当我们改变了变量类型,这个操作就失效了。这种操作与面向对象编程中对象保持独立的生存周期这一原则相悖。
e.g.a , Account = Account,nila.withdraw(100.00)          --error
因为我们将Account赋值为nil了,所以withdraw函数就会报错。
针对上述的操作改进:我们可以传递额外的参数,作为函数运算的对象
e.g.function Account.withdraw(self,v)     self.balance = self.balance - vend
此时
a , Account = Account,nila.withdraw(100.00)          --ok
但是在大多数面向对象编程的语言中,一般都是隐藏我们上述用到的那个参数。Lua也能隐藏这个参数,这里就要使用到冒号操作符
e.g.function Account:withdraw(v)     self.balance = self.balance - vend
当然,这里使用冒号操作符只是一个语法约定而已,没有额外的意思。我们可以用点号运算符定义一个函数然后用冒号运算符调用该函数,反之亦然
e.g.Account = {                    balance = 0,                    withdraw = function (self,v)                                        self.balance = self.balance - v                                     end               }function Account:deposit (v)     self.balance = self.balance + vendAccount.deposit(Account,200)
我个人还是觉得按套路来,遵循这种语法约定。
16.1 Classes
Lua中没有类(class)的概念,但是很容易模仿出类。参考了prototype-base language(面向原型编程)中prototype(原型)的相关技巧。在这种语言中,也是没有类,但是每个对象都拥有一个原型。在这种语言环境下要表现出类的概念,我们只需要为继承者创建一个唯一的对象作为原型。类和原型的目的都在于共享某些行为。
前面在讨论元表的时候有提到继承,因此假如现在有两个对象a和b,采用如下操作便可将b设置为a的原型:
e.g.setmetatable(a,{__index = b})
执行了这个操作之后,假如我们访问a中的成员,在找不到的时候会访问b。
回到现在讨论的类,假如我们需要创建一个新的account,其行为与Account一样,在这里我们就可以考虑使用继承,使用 __index 元方法。在这里我们不需要额外创建一个新的table作为元表,可以直接将我们要继承的table设置为其元表:
e.g.function Account:new(o)     o = o or {}     setmetatable(o,self)     self.__index = self     return oend
这里使用到了前文提到的冒号操作符,默认使用了self参数。
此时
a = Account:new(balance = 0}a:deposit(100.00)
我们新建了一个table  a,其元表为Account,又修改了其元方法__index 为Account 自身,当我们在a中寻找deposit的时候,找不到的时候会自动在Account中寻找,达到了继承的要求。
创建a的时候,将balance赋值为了0,假如不给其赋值,则会继承其默认值
b = Account:new()print(b.balance)               --- 0     继承了Account的balance的值0
16.2 Inheritance
继承
Lua中实现继承还是比较容易的
e.g.--基类Account = {balance = 0 }function Account:new(o)     o = o or {}     setmetatable(0,self)     self.__index = self     return oendfunction Account:deposit(v)     self.balance = self.balance + vendfunction Account:withdraw(v)     if v > self.balance then error "xxx" end     self.balance = self.balance - vend
现在我们想写一个子类继承这个基类,然后能在子类中做进一步的修改,可以这样操作
SpecialAccount = Account:new()
执行以上操作之后,SpecialAccount 便是Account的一个实例了(--modify 应该是继承而非实例吧?),当我们执行一下操作:
s = SpecialAccount:new(limit = 1000.00}
SpecialAccount 从基类中继承了new这个方法,因为这里使用了冒号操作符,默认使用了SpecialAccount这个参数,因此此时s的元表是SpecialAccount。当我们试图访问s中不存在的元素的时候,便会去SpecialAccount中寻找,而从SpecialAccount中寻找不到的时,转而会去Account中寻找。
e.g.s:deposit(100.00)
此时lua会在s、SpecialAccount、Account里面寻找deposit方法
我们可以在子类中重新定义从基类中继承的方法:
e.g.function SpecialAccount:withdraw(v)     if v - self.balance >= self.getLimit() then          error"xx"     end     self.balance = self.balance - vendfunction SpecialAccount:getLimit()     return self.limit or 0end
此时,当我们调用s:withdraw的时候,lua会直接在SpecialAccount找到该方法,执行该方法内的操作。
而lua中有趣的一点是,不需要重新创建一个新的类来实现一个新的行为,可以直接在对象中实现该行为,如:
上文我们已经创建了SpecialAccount对象s,我们要在s中实现一个限制行为,限制每次的操作限额,我们可以这样实现:
e.g.function s:getLimit()     return self.balance * 0.10end
这样,当我们调用s:withdraw的时候,条件判断getLimit会直接得到s已经定义的行为,而不会再去SpecialAccount中寻找。
16.3 Multiple Inheritance
多重继承
Lua中实现面向对象编程是有很多种途径的,上文中提到的使用 __index 元方法是一种便捷的方式。在不同的情况下需要选择不同的实现方式,在这里介绍的是一种能实现多重继承的方法。
这里也涉及到了使用__index 元方法,在该方法内使用一个函数。当table的元表的 __index 字段中有一个函数的时候,Lua都会调用该函数而不管有没有在该table中寻找到key。
多重继承的思想在于一个类可以有多个父类。因此我们就不能用类的方法来创建子类,而是定义一个函数来实现该功能--createClass,以父类作为参数来创建子类。这个函数创建一个table来代表新的类,然后设置元表的元方法__index 来实现多重继承。在这里有要注意的地方,类和父类的关系与类和实例的关系是有差异的,一个类不能同时成为其实例和其子类的元表
e.g.--假定现在有两个类,之前的Account和现在的NamedNamed = {}function Named:getname()     return self.nameendfunction Named:setname(v)     self.name = vend--在plist这个table中寻找klocal function search(k,plist)     for i = 1,#plist do          local v = plist[i][k]          if v then return v end     endendfunction createClass(…)     local c = {}               --新的类     local parents = { … }     --从父类table中找到各个父类中的方法     setmetatable(c,{ __index = function (t,k)          return search(k,parents)     end} )     --多重继承的技巧在于此处,__index 元方法是一个函数,该函数会从父类列表中寻找每个父类中的所有方法,这样就实现了多重继承     --新的类成为其实例的元表     c.__index = c     --创建新的类的构造方法     function c:new(o)          o = o or {}          setmetatable(o,c)          return o     end     return cend
现在我们就能创建一个多重继承的类了:
--多重继承,创建新的类
NamedAccount = createClass(Account,Named)
--创建和使用实例
account = NamedAccount:new{name = "abcd"}print(account:getname())
上述的search函数一定程度上影响性能,以下是作者给的改进:
setmetatable(c,{ __index = function ( t,k )          local  v = search(k,parents)          t[k] = v          return v     end})
一种编程技巧,谨记!
16.4 Privacy
隐私
在已提到的对对象的设计中,并没有提供隐私机制。这是我们使用table来表现对象的结果,也是受影响与Lua本身排斥一些冗余、人为限制的功能。作者的建议是假如不想访问某些值,那么大可以不去访问就是。
Lua的目标是为开发者提供便利,提供多种技巧实现多数需求,尽管设计lua中的对象初衷是不提供隐私机制的,但是可以通过别的方法来实现这个需求——访问控制。这个用的比较少,但还是值得去了解和学习掌握的。
实现这个功能需求在于用两个table来表现对象:一个表示其状态,一个用来表示其操作行为。访问对象的时候通过第二个table进行访问,而对第一个table的设计也有一定的要求,该table并不是存储在别的table中,而是存储在该对象方法的closure中。以此重新设计Account
e.g.function newAccount( initialBalance )     local self = {balance = initialBalance}     local withdraw = function ( v )                         self.balance = self.balance + v                    end     local getBalance = function ( ... )                         return self.balance                    end     return{          withdraw = withdraw,          deposit = deposit,          getBalance = getBalance     }end
在这里该函数首先创建了一个table用来存储内部对象的状态,存储至一个局部变量self。然后该函数内部创建了对象的一系列方法。最后函数创建并返回了另外一个对象,该对象内部存储了实际上要实现的方法的名字。返回的这个新的table应该相当于上文提到的第二个table。这里的核心点在于:这些方法没有使用冒号操作符得到self这个额外的默认参数,而是直接使用了。现在我们可以以一下方式创建新的对象并使用其方法:
e.g.acc1 = newAccount(100.00)acc1.withdraw(40.00)print(acc1.getBalance())
利用这种方式创建的table,我们是没有办法直接访问原table的,只能通过newAccount里面的方法来访问。这样就实现来我们想要的隐私功能。
16.5 The Single-Method Approach
单例的实现
e.g.print("The Single-Method Approach \n") function newObject( value )     return function ( action,v )          if action == "get" then return value          elseif action == "set" then value = v          else error("invalid action")          end     endendd = newObject(0)print(d("get"))d("set",10)print(d("get"))
没有实例,直接通过对象本身访问对象实现的方法。

 

转载于:https://www.cnblogs.com/zhong-dev/p/4044574.html

你可能感兴趣的文章
构建之法读后感
查看>>
hdu题型分类
查看>>
基本信息项目目标文档
查看>>
DNN Web Platform 官方汉化版本 5.5
查看>>
移动开发Html 5前端性能优化指南
查看>>
UGUI 分页渐变居中效果
查看>>
silverlight style和template 使用之tip
查看>>
Eclipse配置python环境
查看>>
第十二周总结
查看>>
Import declarations are not supported by current JavaScript version--JavaScript版本不支持导入声明...
查看>>
js兼容性大全
查看>>
晶振不起振的原因及其解决方法
查看>>
学习目标
查看>>
《利用python进行数据分析》学习笔记--数据聚合与分组(groupby)
查看>>
C++中的函数指针模板
查看>>
2015年个人总结
查看>>
C#编程(六)------------枚举
查看>>
高性能 Windows Socket 组件 HP-Socket v2.3.1-beta-2 发布
查看>>
ZOJ 3316 Game 一般图最大匹配带花树
查看>>
《系统架构师》——操作系统和硬件基础
查看>>