日期: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 - v
end

上述的函数就是OOP中称呼的method(方法)。当然,上述的使用技巧是不可取的:在函数体内使用全局变量Account。这样会造成严重的后果,而且这样使用限制性太大,当我们改变了变量类型,这个操作就失效了。这种操作与面向对象编程中对象保持独立的生存周期这一原则相悖。
e.g.
a , Account = Account,nil
a.withdraw(100.00)          --error

因为我们将Account赋值为nil了,所以withdraw函数就会报错。
针对上述的操作改进:我们可以传递额外的参数,作为函数运算的对象
e.g.
function Account.withdraw(self,v)self.balance = self.balance - v
end

此时
a , Account = Account,nil
a.withdraw(100.00)          --ok

但是在大多数面向对象编程的语言中,一般都是隐藏我们上述用到的那个参数。Lua也能隐藏这个参数,这里就要使用到冒号操作符:
e.g.
function Account:withdraw(v)self.balance = self.balance - v
end

当然,这里使用冒号操作符只是一个语法约定而已,没有额外的意思。我们可以用点号运算符定义一个函数然后用冒号运算符调用该函数,反之亦然
e.g.
Account = {balance = 0,withdraw = function (self,v)self.balance = self.balance - vend}
function Account:deposit (v)self.balance = self.balance + v
end
Account.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 = selfreturn o
end

这里使用到了前文提到的冒号操作符,默认使用了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 = selfreturn o
end
function Account:deposit(v)self.balance = self.balance + v
end
function Account:withdraw(v)if v > self.balance then error "xxx" endself.balance = self.balance - v
end

现在我们想写一个子类继承这个基类,然后能在子类中做进一步的修改,可以这样操作
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() thenerror"xx"endself.balance = self.balance - v
end
function SpecialAccount:getLimit()return self.limit or 0
end

此时,当我们调用s:withdraw的时候,lua会直接在SpecialAccount找到该方法,执行该方法内的操作。
而lua中有趣的一点是,不需要重新创建一个新的类来实现一个新的行为,可以直接在对象中实现该行为,如:
上文我们已经创建了SpecialAccount对象s,我们要在s中实现一个限制行为,限制每次的操作限额,我们可以这样实现:
e.g.
function s:getLimit()return self.balance * 0.10
end

这样,当我们调用s:withdraw的时候,条件判断getLimit会直接得到s已经定义的行为,而不会再去SpecialAccount中寻找。
16.3 Multiple Inheritance
多重继承
Lua中实现面向对象编程是有很多种途径的,上文中提到的使用 __index 元方法是一种便捷的方式。在不同的情况下需要选择不同的实现方式,在这里介绍的是一种能实现多重继承的方法。
这里也涉及到了使用__index 元方法,在该方法内使用一个函数。当table的元表的 __index 字段中有一个函数的时候,Lua都会调用该函数而不管有没有在该table中寻找到key。
多重继承的思想在于一个类可以有多个父类。因此我们就不能用类的方法来创建子类,而是定义一个函数来实现该功能--createClass,以父类作为参数来创建子类。这个函数创建一个table来代表新的类,然后设置元表的元方法__index 来实现多重继承。在这里有要注意的地方,类和父类的关系与类和实例的关系是有差异的,一个类不能同时成为其实例和其子类的元表。
e.g.
--假定现在有两个类,之前的Account和现在的Named
Named = {}
function Named:getname()return self.name
end
function Named:setname(v)self.name = v
end--在plist这个table中寻找k
local function search(k,plist)for i = 1,#plist dolocal v = plist[i][k]if v then return v endend
endfunction 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 oendreturn c
end

现在我们就能创建一个多重继承的类了:
--多重继承,创建新的类

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] = vreturn vend})

一种编程技巧,谨记!
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 + vendlocal getBalance = function ( ... )return self.balanceendreturn{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 valueelseif action == "set" then value = velse error("invalid action")endend
endd = newObject(0)
print(d("get"))
d("set",10)
print(d("get"))

没有实例,直接通过对象本身访问对象实现的方法。

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

《Programming in Lua 3》读书笔记(十二)相关推荐

  1. Java 读书笔记 (十二) Java Character 类

    在实际开发过程中, 我们经常会遇到需要使用对象,而不是内置数据类型的情况. 为了解决这个问题, Java语言为内置数据类型char提供了包装类Character类. 可以使用Character的构造方 ...

  2. 深入理解Java虚拟机读书笔记十二

    第 7 章虚拟机类加载机制 7. 1.概述 虚拟机把描述类的'数据从Class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. ...

  3. 主成分分析碎石图_ISLR读书笔记十九:主成分分析(PCA)

    本文使用 Zhihu On VSCode 创作并发布 前面写的一些统计学习方法都是属于监督学习(supervised learning),这篇主成分分析(principal components an ...

  4. OREILLY Programming .NET 3.5 读书笔记之一

    OREILLY Programming .NET 3.5 读书笔记之一 <Programming .NET 3.5>是OREILLY 2008.08出版的.NET 3.5 开发书籍,作者是 ...

  5. Python语言入门这一篇就够了-学习笔记(十二万字)

    Python语言入门这一篇就够了-学习笔记(十二万字) 友情提示:先关注收藏,再查看,12万字保姆级 Python语言从入门到精通教程. 文章目录 Python语言入门这一篇就够了-学习笔记(十二万字 ...

  6. 曾国藩读书的十二条规矩

    曾国藩读书的十二条规矩 曾国藩,清代名臣,他既是镇压太平天国将士的刽子手,又是一个治学严谨.博览群书的理论家和古文学家. 曾国藩是湖南湘乡县(今双峰县)人,清嘉庆十六年(1811年)出生于一个穷山僻谷 ...

  7. 深入分析Java Web技术内幕读书笔记(二)浅析DNS域名解析过程

    上一篇文章<浅析Web请求过程>讲述的是如何发起HTTP请求,对于请求发起过程中很重要的一个步骤--DNS解析过程的描述是一带而过,本篇文章将跟着DNS解析过程来分析域名是如何解析的. 一 ...

  8. Linux设备驱动程式之读书笔记(二) [转]

    http://www.sudu.cn/info/html/edu/code/20070102/286543.html setlevel.c 非常实用 google 查找 #define _syscal ...

  9. 重构(Refactoring)技巧读书笔记 之二

    重构(Refactoring)技巧读书笔记 之二<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:of ...

最新文章

  1. linux写命令时方便的快捷键以及Vim必备命令
  2. [Tool]靜態程式碼分析-FxCop
  3. 解决This picacion faied to trt becuse t could, not find or load the Qt platform plugin “windows““问题
  4. ConcurrentLinkedQueue
  5. 深度学习笔记(35) 滑动窗口的卷积实现
  6. docx文档怎么排列图片_“胶水语言”办公自动化Word篇——使用Python编辑和读取Word文档
  7. Spring学习总结(27)——Spring常用注解再总结
  8. JavaScript把客户端时间转换为北京时间
  9. python3中解码base64(线下base64解码模板)
  10. Android中英文单词录入背诵软件
  11. 【微软小冰】多轮和情感机器人的先行者
  12. C语言找n以上最小质数,[原创]求质数(C语言描述)
  13. matlab将txt转成dat,将matlab中数据保存为txt或dat格式
  14. 洛谷T30768 动感超人520
  15. 足疗小张和面向对象的7个设计原则
  16. 接口管理工具Rap的安装
  17. PCB板不同材质区别
  18. 和量子计算有什么区别 并发_超级计算机和量子计算机有什么区别?
  19. mysql 二进制 nodejs_Linux 下安装NodeJS (二进制包)
  20. git-lfs使用笔记

热门文章

  1. php创建表并插入数据,php数据库操作-创建库和表以及插入数据
  2. 第3章 Python 数字图像处理(DIP) - 灰度变换与空间滤波4 - 分段线性变换 - 对比度拉伸
  3. Python利用matplotlib.animation和matplotlib.pyplot和ffmpeg录制动画并保存为MP4文件
  4. POJ 1723 Soldiers (中位数)
  5. 修改SQL server数据库中的逻辑文件名
  6. 全国计算机等级考试题库二级C操作题100套(第60套)
  7. rh php56 php,在全球范围内提供RHSCL PHP的最佳方法
  8. yii2 php反射,Yii2.0-advanced-3—为yii2添加后台模板adminlte和权限组件yii2-adm
  9. windows os x linux,What languages are Windows, Mac OS X and Linux written in?
  10. 开发工具:Git和SVN有哪些差异,看完你就懂了?