面向对象概述
面向对象编程技术是目前最常用的编程技术之一。目前大量的编程语言都支持面向对象的特性:
- C++
- Java
- Objective-C
- Smalltalk
- C#
- Ruby
面向对象的特征
- 类(class):类是可以创建对象,并为状态(成员变量)提供初值及行为实现的可扩展模板。
- 对象(objects):对象是类的实例,每个对象都有独立的内存区域。
- 继承(inheritance):继承用于描述一个类的变量和函数被另一个类继承的行为。
- 封装(encapsulation):封装是指将数据和函数组织在一个类中。外部可以通过类的方法访问内中的数据。封装也被称之为数据抽象。
Lua 中的面向对象
在 Lua 中,我们可以使用表和函数实现面向对象。将函数和相关的数据放置于同一个表中就形成了一个对象。继承可以用元表实现,它提供了在父类中查找存在的方法和变量的机制。
Lua 中的表拥有对象的特征,比如状态和独立于其值的标识。两个有相同值的对象(表)是两个不同的对象,但是一个对象在不同的时间可以拥有不同的值。与对象一样,表拥有独立于其创建者和创建位置的生命周期。
一个真实世界的例子
面向对象已经是一个广泛使用的概念,但是你需要正确清楚地理解它。
让我们看一个数学方面的例子。我们经常需要处理各种形状,比如圆、矩形、正方形。
这些形状有一个共同的特征——面积。所以,所有其它的形状都可以从有一个公共特征——面积的基类扩展而来。每个对象都可以有它自己的特征和函数,比如矩阵有属性长、宽和面积,printArea 和 calculateArea 方法。
创建一个简单的类
下面例子实现了矩阵类的三个属性:面积、长和宽。它还同时实现了输出面积的函数 printArea。
-- 元类 Rectangle = {area = 0, length = 0, breadth = 0} -- 继承类的方法 new function Rectangle:new (o,length,breadth) o = o or {} setmetatable(o, self) self.__index = self self.length = length or 0 self.breadth = breadth or 0 self.area = length*breadth; return o end -- 继承类的方法 printArea function Rectangle:printArea () print("The area of Rectangle is ",self.area) end
创建对象
创建对象即是为类的实例分配内存空间的过程。每个对象都有自己独立的内存区域,同时还会共享类的数据。
r = Rectangle:new(nil,10,20)
访问属性
我们可以使用点操作符访问类中属性。
print(r.length)
访问成员方法
使用冒号操作符可以访问对象的成员方法,如下所示:
r:printArea()
初始化阶段,调用函数为对象分配内存同时设置初值。这与其它与面向对象的语言中的构造器很相似。其实,构造器本身也就和上面的初始化代码一样,并没有什么特别之处。
完整的例子
让我们一起看一个 Lua 实现面向对象的完整例子。
-- 元类 Shape = {area = 0} -- 基类方法 new function Shape:new (o,side) o = o or {} setmetatable(o, self) self.__index = self side = side or 0 self.area = side*side; return o end -- 基类方法 printArea function Shape:printArea () print("The area is ",self.area) end -- 创建对象 myshape = Shape:new(nil,10) myshape:printArea()
运行上面的程序,我们可以得到如下的输出结果:
The area is 100
Lua 中的继承
继承就是从基对象扩展的过程,正如从图形扩展至矩形、正方形等等。在现实世界中,常用来共享或扩展某些共同的属性和方法。
让我们看一个简单的类扩展的例子。我们有如下的类:
-- 元类 Shape = {area = 0} -- 基类方法 new function Shape:new (o,side) o = o or {} setmetatable(o, self) self.__index = self side = side or 0 self.area = side*side; return o end -- 基类方法 printArea function Shape:printArea () print("The area is ",self.area) end
我们从上面的类中扩展出正方形类,如下所示:
Square = Shape:new() -- 继承类方法 new function Square:new (o,side) o = o or Shape:new(o,side) setmetatable(o, self) self.__index = self return o end
重写基类的函数
继承类可以重写基类的方法,从而根据自己的实际情况实现功能。示例代码如下所示:
-- 继承方法 printArea function Square:printArea () print("The area of square is ",self.area) end
继承的完整示例
在元表的帮助下,我们可以使用新的 new 方法实现类的扩展(继承)。子类中保存了所有基类的成员变量和方法。
-- Meta class Shape = {area = 0} -- Base class method new function Shape:new (o,side) o = o or {} setmetatable(o, self) self.__index = self side = side or 0 self.area = side*side; return o end -- Base class method printArea function Shape:printArea () print("The area is ",self.area) end -- Creating an object myshape = Shape:new(nil,10) myshape:printArea() Square = Shape:new() -- Derived class method new function Square:new (o,side) o = o or Shape:new(o,side) setmetatable(o, self) self.__index = self return o end -- Derived class method printArea function Square:printArea () print("The area of square is ",self.area) end -- Creating an object mysquare = Square:new(nil,10) mysquare:printArea() Rectangle = Shape:new() -- Derived class method new function Rectangle:new (o,length,breadth) o = o or Shape:new(o) setmetatable(o, self) self.__index = self self.area = length * breadth return o end -- Derived class method printArea function Rectangle:printArea () print("The area of Rectangle is ",self.area) end -- Creating an object myrectangle = Rectangle:new(nil,10,20) myrectangle:printArea()
运行上面的程序,我们可以得到如下的输出结果:
The area is 100 The area of square is 100 The area of Rectangle is 200
上面的例子中,我们继承基类 Shape 创建了两个子类 Rectange 与 Square。在子类中可以重写基类提供的方法。在这个例子中,子类重写了 printArea 方法。