• 新浪微博:
  • 微信 :
按键精灵电脑版
立即下载

软件版本:2014.06
软件大小:22.9M
更新时间:03-18

按键精灵安卓版
立即下载

软件版本:3.3.8
软件大小:62.5M
更新时间:12-18

按键精灵iOS版
立即下载

软件版本:1.7.1
软件大小:29.2M
更新时间:06-22

最新企业版UiBot
立即下载

软件版本:3.3
软件大小:282M
更新时间:08-06

快捷导航

登录 后使用快捷导航
没有帐号? 注册

登录 注册
发新话题 回复该主题

果壳lua教程(第五集) :面向对象编程-制作一个钟表 [复制链接]

1#

>你以为还会是开头一段代码吗?然而并不是~

emmm~小茗童鞋,咱们今天来讲讲如何通过面向对象的方式来制作一块手表!

辣么,构成手表这一对象需要的哪些要素呢?

小茗同学:手表这东东,当然要由时、分和秒这三个指针来构成啦!

没错!所以呢,我们就用3个变量(Hour、Minute、Second)分别代表手表的这三大属性:时、分、秒

  1. local Hour=0

  2. local Minute=0

  3. local Second=0
复制代码

>上述代表通过local来分别声明了3个临时变量,并且在声明的同时还给变量赋了初值。

小茗同学:这么说来,这块手表的3个属性(时、分、秒)的初始值都为0。

是的没错!接下来,我们思考这个问题:一块手表除了只有时、分、秒的指针外,就完事了? 如果它是一件没有实际功能的艺术品的话,确实就完事了!那么这件艺术品打造出来后,时分秒的初始值就是0,3个指针会重叠在一个位置。

小茗同学:这件艺术品真是太呆板了,毫无活力!

哈哈!辣么,我们现在就让它复活起来!首先要明确的是,如果想打造一块真正会动的手表,就不能只给它赋值属性值,还需要赋予它功能!

小茗同学:啥功能?

想想看,一块手表需要具备的基本功能会是什么? 在本例中,我们只例举两大功能:1.时间设置的功能。2.时间显示的功能。

小茗同学:噢,那快告诉我,这俩功能如何实现吧!

好的,请看下边代码:

  1. local function SetTime(self,NewH,NewM,NewS)

  2. self.Hour=NewH

  3. self.Minute=NewM

  4. self.Second=NewS

  5. end
复制代码

>我们定义了一个临时函数SetTime,该函数就是实现设置时间的功能!

小茗同学:我大概看懂了,这SetTime函数的参数包括了分别用于接收时分秒的变量:NewH、NewM、NewS。可是这第一个参数self代表什么,我就不知道了!

问得好,这self啊,百度查下翻译不就知道了,是“自身”之意。那么问题来了:谁的自身?

小茗同学:唔~完全蒙B。

我们先尝试这样理解:self代表之前定义的那3个初始变量:

  1. local Hour=0

  2. local Minute=0

  3. local Second=0
复制代码

>换句话说,如果传递过来的新属性值是数值型的,且不是nil(空)值的话,那么我们就将新值覆盖初始值。

举个栗子:传进的NewH值是3,那么3就会覆盖Hour的初值0,因此,为实现以上效果,我们需要稍微修改下代码,请看下面代码:

  1. function clock(NewH, NewM, NewS)

  2. if type(NewH)~="number" or NewH ==nil then

  3. NewH = Hour

  4. end

  5. if type(NewM)~="number" or NewM ==nil then

  6. NewM = Minute

  7. end

  8. if type(NewS)~="number" or NewS ==nil then

  9. NewS = Second

  10. end

  11. local obj = {

  12. Hour = NewH,

  13. Minute = NewM,

  14. Second = NewS,

  15. ["SetTime"] = SetTime,

  16. ["ShowTime"] = ShowTime,

  17. }

  18. return obj

  19. end
复制代码

>如此便能对传入的NewH、NewM、NewS进行判断是否为非数值型,是否为空值

然后再将每个传入的值一一赋予Hour、Minute、Second

小茗同学:那么,Hour、Minute、Second前缀都有个self. 可以理解为代表这块手表最初定义的那3个属性?

是的!self正是代表这块手表本身,或者说,self表示对象自身。

而你传入的新的属性值必须通过覆盖此对象自身的原始属性值后,才能发挥作用

  1. self.Hour=NewH

  2. self.Minute=NewM

  3. self.Second=NewS
复制代码

>所谓“覆盖此对象自身的原始属性值”,就是一个简单的赋值操作,只不过接收值的变量必须为该对象本身的原始变量,所以我们用self前缀。

小茗同学:哇噢,看起来像玄学哟!

认真点,真相只有一个:self就是代表手表这一对象的最原始的成员变量!

小茗同学:又学到一个新词——成员变量?

成员变量就是构成手表这一对象的属性部分的数据成员:Hour、Minute、Second

小茗同学:噢,记住了!

那么再教你两个新词:成员函数和方法——它们同指一个东西——那就是前面提到的实现了手表的显示时间功能和设置时间功能的函数!

小茗同学:我明白了,前面提到的SetTime函数也可以称之为手表这个对象里的成员函数,或者方法?

是的!一般我们称之为对象方法,用于实现对象的某一功能。

小茗同学:那前面已经讲完SetTime显示时间的功能,接下来是不是该讲讲设置时间的功能了?

很好!小茗最近营养和思维同时跟上了! 辣我们现在就接着往下讲显示时间功能的函数,请看下面代码:

  1. local funtion ShowTime(self)

  2. if self.Hour==24 then self.Hour=0 end

  3. print(self.Hour.."时 ",self.Minute.."分 ")

  4. end
复制代码

>了解了之前的设置时间功能后,再了解显示时间功能,会发觉相当简单!

小茗同学:嗯!看懂了,就是直接调用ShowTime函数,然后该函数内直接输出了手表的时分秒

基本正确,但要完整、科学地说是:该函数输出的是手表对象中此刻已存储的最新的时(Hour)、分(Minute)、秒(Second)的成员变量的值

小茗同学:也就是说,如果事先通过SetTime设置时间函数将最新的时分秒设置进去后再输出的话,就会显示最新的时分秒喽?

是的,由此我们可以通过这种设置----显示----再设置----再显示的方式来模拟现场中真实手表的状态和功能效果,这就是:面向对象编程的思想

小茗同学:哇噢,老湿您刚才说面向对象编程的思想?那是一种思想吗?

是的,面向对象是一种编程思想,而不是编程技巧,在编程的世界里,有时候思想比技巧更为重要,掌握了思想,心中就有了蓝图,然后再通过技巧一点点去勾画、构建、实现这张蓝图!

小茗同学:哇噢,听起来还真不像玄学,心中有一股茅塞顿开的感觉!

好!接下来要讲重点了!且看下面代码

  1. do

  2. -- 私有数据成员

  3. local Hour = 0

  4. local Minute = 0

  5. local Second = 0

  6. --方法 设置时间

  7. local function SetTime(self,NewH, NewM, NewS)

  8. if type(NewH)~="number" or NewH ==nil then

  9. NewH = Hour

  10. end

  11. if type(NewM)~="number" or NewM ==nil then

  12. NewM = Minute

  13. end

  14. if type(NewS)~="number" or NewS ==nil then

  15. NewS = Second

  16. end

  17. self.Hour=NewH

  18. self.Minute=NewM

  19. self.Second=NewS

  20. end

  21. --方法 显示时间

  22. local funtion ShowTime(self)

  23. if self.Hour==24 then self.Hour=0 end

  24. print(self.Hour.."时 ",self.Minute.."分 ")

  25. end

  26. --构造函数 钟表

  27. function clock(NewH, NewM, NewS)

  28. if type(NewH)~="number" or NewH ==nil then

  29. NewH = Hour

  30. end

  31. if type(NewM)~="number" or NewM ==nil then

  32. NewM = Minute

  33. end

  34. if type(NewS)~="number" or NewS ==nil then

  35. NewS = Second

  36. end

  37. local obj = {

  38. Hour = NewH,

  39. Minute = NewM,

  40. Second = NewS,

  41. ["SetTime"] = SetTime,

  42. ["ShowTime"] = ShowTime,

  43. }

  44. return obj

  45. end

  46. end
复制代码

>小茗同学:这是啥玩艺儿,看起来好复杂的样子!

这是构成手表这个对象的最核心的部分,它组成了一个称之为类的东西,所有的成员变量和成员函数都要通过这个核心的东西(类)来进行组装!我们举个栗子来帮助理解一下这个概念:想你一台电脑主机,它由网卡、显卡、内存条、主板、硬盘等组成,那么我们总不能将这些零件通通散敌的堆在一堆或排成一列吧? 我们需要有序的将他们组装起来,于是它们被组装在一个机箱里,那么这个构造函数我们可以姑且理解为是包装所有组成手表这一对象的载体——事实上,这一载体以及组成它的所有零配件,我们统一称之为类——它就是手表类!

下图将展现一个完整的手表类,注意它由do----end 这个模块包装而成,在do----end模块里边,所有用local声明的变量或定义的函数都是局部的(其作用域仅限于此模块中)

通过此图我们可以看出一个完整的手表类是怎么构建的,我们把目光再转向构造函数这部分。

该函数名为clock,是手表类中的核心部分,其用途可以把它想像为一张蓝图,当我们生产具体的手表时,施工人员要根据这张蓝图来施工,从而生产出一个个相同或各不相同的手表(例如手表的属性初始值不同,初始值包括手表的颜色、形状大小等,当然本例中的属性初始值仅为时分秒的初始值),辣么无论是什么功能的、什么形状的、什么颜色的手表,都通过一张设计蓝图去实现(去生产),可想而知,这张蓝图多么重要。正如之前所说的蓝图就是类,对象是根据蓝图设计生产出来的实体,所以说,类是抽象的,对象是具体的,对象由类而来,对象是类的实例化。

小茗同学:哇噢,又接近玄学了~

玄你个头!只要拓展思路,发挥联想,贴近生活,就不会被困住,脚本让我们解放双手,而面向对象编程则让我们解放思维,让我们体味到编程对现实世界的高度模拟和仿真的乐趣。

小茗同学:我造了!听说编写游戏都是用面向对象的,难怪游戏世界里的物理现象可以做到如此逼真,但是我并不完全认识这是游戏世界对现实世界的高度模拟,而应该说是游戏世界在对现实世界的模拟仿真基础上,再进行艺术和想像力的加工,有些科幻类的游戏便是如此!lol也是如此,各种英雄技能既带有很真实的物理性,又融入了设计师的想像力和趣味性。

说得对!小茗不愧是LOL青铜组最有见识的选手了,没有之一!

话说你刚才提到的想像力和趣味性,我们可以把这两种东西统称为一种东西,那便是——创造力!

小茗同学:嗯!嗯!

所以说,在外行人眼里,我们敲代码的都是生活中不解风情的直男直女,其实不然,在我看来,编程思想源于生活,高于生活!

好了,言归正专!我们来看下构造函数里的这一段代码:

  1. local obj = {

  2. Hour = NewH,

  3. Minute = NewM,

  4. Second = NewS,

  5. ["SetTime"] = SetTime,

  6. ["ShowTime"] = ShowTime,

  7. }

  8. return obj
复制代码

>这是一个表,当我们每次调用构造函数时,函数都会返回一个表的地址(也就是返回了这个表本身),那么问题来了,这个表里边携带了什么信息呢?

小茗同学:正如代码中所示,这个表中携带了时分秒的属性值信息和……老湿["SetTime"]=SetTime这是啥意?

呵呵,["SetTime"]=SetTime和["ShowTime"]=ShowTime 都表示,分别给表中的["SetTime"]成员和["SetTime"]成员赋值,赋予了什么值?不是普通的变量值哟,而是一个函数的地址(也就是这该函数本身),之前不是提到过ShowTime函数和SetTime嘛,那么现在就是要将这俩函数赋给俩成员。表的名称叫obj,如果要访问表成员,则采用打点调用方式即可,如:obj.SetTime。或非打点调用方式:obj["ShowTime"]、obj["Hour"]。

那么接下来,我们要讲另一个重点:实例化一个对象

  1. --实例化clock类,创建myClock对象,并在创建时通过构造函数给对象的个数据成员赋初值,构造了一个具备时,分,秒初始值的钟表

  2. myClock = clock(12, 1, 1)

  3. --调用ShowTime方法显示钟表的时,分,秒

  4. myClock:ShowTime()

  5. myClock:SetTime(15, 33, 44)

  6. --myClock.SetTime(myClock)

  7. myClock:ShowTime()
复制代码

>对象名叫myClock,其实它就是一个普通变量,我们用它来接收了clock的构造函数的返回值,构造函数故名思义就是在制造一个对像出来之时就已经构造了该对象的一些初值数据,本例中构造对象的初始数据分别是时分秒的初始数据:myClock=clock(12,1,1)

于是myClock就得到了一个obj表,该表中就包括了构成手表这个对象的所有信息了(包括属性和方法两部分)

那我们用myClock这个对象来干嘛呢?

第一,设置时间:myClock:SetTime(15,33,44)

第二,显示时间:myClock:ShowTime()

于是这个对象就为我们所用了,这就是从手表蓝图中生产出来的第一块手表,当然你还可以继续另一个手表对象,只需将对象名改一下,例如我们再创建一个称为myClock2的对象:myClock2=clock(9,23,11)

看到了吧,同一张手表蓝图纸(类),可生产出不同的手表(对象)

小茗同学:哇噢,老湿我有一事不明,myClock:ShowTime() 为什么这样的代码看起来好奇怪?

这是因为,这种写法利用了lua中的语法糖效果

小茗同学:啥叫语法糖,这种糖能吃吗!

你就知道吃,语法糖呢,一时半会也说不清楚,就这么简单的理解吧:服用之后能让代码看起来更简洁。

现在,我们来还原一下这段代码myClock:ShowTime() 在服用语法糖之前的本来面目:myClock.ShowTime(myClock)

小茗同学:哇噢,越看越玄!

它的意思呢,也很简单,我们知道myClock是一个由类而生的具体对象,而它实际上是一个表(因为当创建它的时候返回了一个表地址),那么ShowTime是表中的成员,该成员是一个函数,该函数的功能是显示时间,所以,myClock.ShowTime()就调用了表中的这一函数。

接下来我们再回顾一个ShowTime函数:ShowTime(self)。 看到了吧,这个显示时间的函数带有一个self的参数,self在前面已经讲过它是代表其自身的意思,那么问题来了,代表谁的自身?正是代表手表类中的obj自身!而obj的返回值是由myClock对象接收,因为当我们调用myClock.ShowTime()时,我们必须将其自身(也就是myClock)代入其中!所以就写成了myClock.ShowTime(myClock) ,它的语法糖格式就是:myClock:ShowTime()

小茗同学:那么那么,myClock:SetTime(15,33,44)也是如此喽,它的非语法糖写法就是:myClock.SetTime(myClock,15,33,44)

没错!小茗!你终于领悟到了”玄学“的最高境界!

小茗同学:哇噢~开心ing


本主题由 按键合作伙伴 果壳王子 于 2020/3/31 22:52:09 执行 移动主题 操作
果壳学院 ● 疯狂编程 QQ交流群: 327610461 (编程要从娃娃抓起)

  果壳热编程教育网:www.guokeHOT.com ←点击登录,即刻学习!

发新话题 回复该主题