一、面向对象特征
- 封装:指能够把一个实体的信息、功能、响应都装入一个单独的对象中的特性。
- 继承:继承的方法允许在不改动原程序的基础上对其进行扩充,这样使得原功能得以保存,而新功能也得以扩展。这有利于减少重复编码,提高软件的开发效率。
- 多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
- 抽象:抽象(Abstraction)是简化复杂的现实问题的途径,它可以为具体问题找到最恰当的类定义,并且可以在最恰当的继承级别解释问题。
二、Lua实现面向对象的原理
我们知道,对象由属性和方法组成。LUA中最基本的结构是table,所以需要用table来描述对象的属性。lua中的function可以用来表示方法。那么LUA中的类可以通过table + function模拟出来。至于继承,可以通过metetable模拟出来(不推荐用,只模拟最基本的对象大部分时间够用了)。
Lua中的表不仅在某种意义上是一种对象。像对象一样,表也有状态(成员变量);也有与对象的值独立的本性,特别是拥有两个不同值的对象(table)代表两个不同的对象;一个对象在不同的时候也可以有不同的值,但他始终是一个对象;与对象类似,表的生命周期与其由什么创建、在哪创建没有关系。
三、类的属性和方法创建
下面以创建一个简单的Person类,该类包含了两个属性一个方法。
person = { name = "leander",age = 24}
--1.采用.形式定义方法(形式一)
--self代表当前的表,也就是person
person.DoWork1 = function(self)
print(self.name.."在写代码!");
end
--与形式一对应的方法调用,需要显式传入第一个参数person
person.DoWork1(person);--leander在写代码!
--2.采用:形式定义方法(形式二) 此时没有参数self依然可以顺利调用
--self代表当前的表,也就是person
function person:DoWork2()
--此时,即使没有传入self参数,依然可以通过self访问它的属性
print(self.name.."写代码!");
end
--与形式二对应的两种方法调用
--当采用.方式进行调用时,此时第一个参数self不可被省略,后续传入方法参数
person.DoWork2(person);--此时若不传入person参数,则报错
--当采用:方式进行调用时,此时第一个参数self可以被省略,后续传入方法参数
person:DoWork2();--leander写代码
在创建该类属性的时候,与其他高级语言类似,但是对于创建方法时,还是有很大区别的。而且创建的方法均是其该类的对象上的方法,而不是类的自身方法。在定义的形式上,分为两种:1.采用.形式定义方法(形式一)。2.采用:形式定义方法(形式二)。二者的区别在于:当需要使用类的属性时,形式一需要传入当前对象参数,而且必须显式传入;而形式二,当前对象参数可省略。在调用方法时,与创建的形式对应。在这里需要指出的是,采用形式二定义的方法,既可以通过”:”的形式进行调用,可以通过”.”的形式调用。
三、类的构造方法实现
通过利用原表实现构造函数,第一种,构造函数不带参数。
function person:new()
local t = {};
setmetatable(t,{__index = self});
return t;
end
person1 = person:new();
person2 = person:new();
--当person1为空的是,返回为原person.name的值,即leander
print(person1.name); --leander
person1.name = "leander 2号";
--经过上述修改之后,person1存在了name属性的值,因此,不再依赖原person.name的值
print(person1.name);--leander 2号
person1:DoWork2();--此时,leander 2号在写代码!
此时,新创建的对象person1,person2与person的关系为,person1和person2的元表的__index属性被赋值成了person。因此,当读取person1或person2的不存在属性或方法时,则去person表中查找,若找到,则读取该属性或执行该方法。通过新创建的对象person1对person属性修改时,并不影响person的属性值。此时,相当于在person1中新建了一个相同名字属性字段,不再依赖于person对应的属性。
第二种,构造函数带参数。
function person:new(o)
--该参数若为nil,则直接构造一个新表,否则是对原有表的扩充
local t =o or {};
--setmetatable(t,{__index = self});
--上述一条语句等价于下面两条语句
setmetatable(t,self);
self.__index = self;
return t;
end
--此时,利用person构造函数没有创建新表,而是在表{weight=100}的基础上扩建的
person3 =person:new({weight=100});
print(person3.name);
print(person3.age);
print(person3.weight);--100
--当不传入参数时,则创建一个新表,此时参数默认为nil
person3 =person:new();--不传入参数等价于传入nil参数
print(person3.name);
print(person3.age);
print(person3.weight);--nil
带参数的构造函数优势在于,你可以在原来表的基础上扩充属性和方法,这里面也有点儿继承的味道。下面来实现Lua的继承。
三、类的继承实现
继承是指一个对象直接使用另一对象的属性和方法。可用于扩展基础类的属性和方法。
person = { name = "leander",age = 24}
--基础person类构造函数new
function person:new(o)
--该参数若为nil,则直接构造一个新表,否则是对原有表的扩充
local t =o or {};
setmetatable(t,self);
self.__index = self;
return t;
end
-- 基础类方法 DoWork
function person:DoWork()
print(self.name.."在写代码!");
end
--实现student对象继承person类
student = person:new();
--重写student的构造方法
function student:new(o,grade)
--该参数若为nil,则直接构造一个新表,否则是对原有表的扩充
local t =o or {};
t.grade = grade;
setmetatable(t,self);
self.__index = self;
return t;
end
function student:StudentDoWork(grade)
--此处,也可通过self.grade属性来获取值
print("在教"..grade.."年级student写代码!");
end
--[[
--重写父类方法
-- student类方法 DoWork
function student:DoWork()
print(self.name.."不想写代码!");
end
--]]
stu1 = student:new(nil,3);
--打印,子类的属性
print(stu1.grade);--3
--打印,父类的属性
print(stu1.name);--leander
--调用父类person方法
stu1:DoWork();--leander在写代码!
--调用自身的方法
stu1:StudentDoWork(stu1.grade);