网站维护与推广,广州最新防疫动态,公司网站建设亚运村,抖音头像的网站制作教程日期#xff1a;2014.7.14 PartⅡ Object-Oriented ProgrammingLua中实现面向对象编程。“如同OOP对象#xff0c;table拥有状态#xff1b;如同OOP对象#xff0c;table拥有标识符---self#xff0c;用来与其他变量做区分#xff0c;而且两个table拥有同样的值也是…日期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() thenerrorxxendself.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