lua学习06——lua中的面向对象

来源:互联网 发布:360浏览器有没有mac版 编辑:程序博客网 时间:2024/05/16 00:45

本篇文章,主要来讲述,lua中的面向对象是如何实现的。


1. 对象-table

lua中的对象,其实之前在介绍类型的时候就稍微提及过一些 ———— table。
有三个理由,让table可以胜任对象

  • table与对象一样可以拥有状态
  • table与对象一样拥有一个独立与其值的标识(self)
  • table与对象一样具有独立于创建者和创建地的生命周期
    我们要在一个对象中,设置一个函数:
Person = {name = "Tom"}  function Person.changeName(newName)    Person.name = newName  end  Person.changeName("tree")  print(Person.name)  -- result  tree

这样的函数就是方法,但是,如果方法这么写,是非常不好的。
因为如果我们讲对象名字换掉了,那么相应的方法就不能用了:

p = PersonPerson = nilp.changeName("Jerry")print(p.name)-- resultnil

这违反了之前的第三条——对象拥有独立的生命周期。
这时候,就要用到self了,它跟C++中的this指针相像。

function Person.changeName(self, newName)   self.name = newName end p = Person Person = nil p.changeName(p, "Jerry") print(p.name)  -- result  Jerry

但是,每次调用的时候,都要显式的把相应对象传进去,是不是很麻烦呢?
大多数面向对象的语言都能对程序员隐藏部分self参数,lua也不例外,它只需要用冒号:

 function Person:changeName(newName)    self.name = newName  end  ...  p:changeName("Jerry")

就是这么简单。


2.类

lua中没有类的概念,每个对象只能自定义行为和形态。
不过,要在lua中模拟类也并不困难,可以参照一些基于原型的语言,
基于原型的语言中,对象是没有”类型”的,而是每个对象都有一个原型,原型也是一种常规的对象。
当对象执行一个未知操作时,原型会先查找它。
在lua中的实现,用到了元表(因为查找一个,找不到查找另一个 -> 这种行为,很典型的元表)

 -- 一个原型  local Person = {}  function Person:new(t)    t = t or {}    setmetatable(t, self)    self.__index = self    return t  end  local p = Person:new({name = "Tom"})  p:showName()

我们,首先用Person:new创建了一个新的实例对象,然后将Person作为新的实例对象p的元表。
再当我们调用 p:showName 函数时就会查找p中是否有showName字段,如果没有,就去搜索它的元表
一个类不仅可以提供方法,还可以为实例中的字段提供默认值:

 local Person = {name = "Tom", age = 0}  function Person:new(t)    t = t or {}    setmetatable(t, self)    self.__index = self    return t  end  function Person:showDetail()    self.age = self.age + 1    print("name: "..self.name.." ,age: "..self.age)  end  local p = Person:new()  p:showDetail()  p:showDetail()

在Person表中有一个 age的字段,我们默认为0;当我们创建一个实例对象,没有提供关于age的字段,在showDetail函数中,由于p中没有age字段,向上查找到Person,最后得到的是Person的age值。


3.继承

因为类也是对象,所以它也可以从其它类获得方法。
假设,我们有个类Person,然后要派生出一个类Student:

local Person = {name = "Tom", age = 0}function Person:new(t)  t = t or {}  setmetatable(t, self)  self.__index = self  return tendfunction Person:setAge(age)  self.age = ageendfunction Person:setName(name)  self.name = nameendfunction Person:showDetail()  print("Name: "..self.name..",Age: "..self.age)endlocal Student = Person:new()              -- 1local s = Student:new({name = "Jerry"})   -- 2s:showDetail()

在这段代码中,我们在开始构建一个”基类” Person,给它设置一些方法(字段)
然后,在 1 中,我们创建了Person的实例对象——Student,但是Student也是一个类。
在 2 中,我们用Student派生出s。
这时,s执行showDetail函数,当在s中找不到相应字段,因为s继承自Student,便会去Student去寻找,当在Student也找不到时,会寻找Student的父类——Person。
但是,如果我们在s或者Student重新定义了一个showDetail函数,它便会覆盖父类的,不会继续往下找。
这,就是继承体系。


4.多重继承

多重继承,简单的了解一下。
我们实现单一继承的方法,是利用了lua中的元表,在派生类中找不到字段,设置metatable,让其去父类寻找,并将父类的__index设置为自身。
多继承也可以这样用,继承,就是为了利用父类相应的方法,多继承,就是在多个父类中寻找相应的方法(字段),我们可以将__index设置一个函数,让它遍历所有父类。


5.权限

在面向对象中,我们的类成员是有访问权限的。
最基础的: private、protect、public
lua是一个简单的脚本语言,它尽量的压缩自己的代码,显然本身不会带这些机制。
但,没有枪、没有炮、我们自己造啊~

function student(name)  local self = {m_name = name}  local setName = function (name)    self.m_name = name  end  local getName = function ()    return self.m_name  end  return {setName = setName, getName = getName}endlocal stu = student("Tom")print(stu.getName())stu.setName("Jerry")print(stu.getName())

这里,我们创建一个函数,然后在函数内部,自己构建一套机制来设置、获取,返回相应的方法。
当我们获得 student的返回以后,我们就无法直接访问这个table,只能通过函数来访问。
用这种方法来保护类成员

0 0
原创粉丝点击