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

本篇文章,主要来讲述,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 = Person
Person = nil
p.changeName("Jerry")
print(p.name)

-- result
nil

这违反了之前的第三条——对象拥有独立的生命周期。
这时候,就要用到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 t
end

function Person:setAge(age)
  self.age = age
end

function Person:setName(name)
  self.name = name
end

function Person:showDetail()
  print("Name: "..self.name..",Age: "..self.age)
end


local Student = Person:new()              -- 1
local s = Student:new({name = "Jerry"})   -- 2
s: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}

end

local stu = student("Tom")
print(stu.getName())
stu.setName("Jerry")
print(stu.getName())

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值