Lua 面向对象
Lua - 面向对象编程
Section titled “Lua - 面向对象编程”OOP 概念简介
Section titled “OOP 概念简介”面向对象编程(Object-Oriented Programming, OOP)是一种基于“对象”概念的编程范式。对象可以包含以字段形式(通常称为属性或特性)存在的数据,以及以过程形式(通常称为方法)存在的代码。OOP 的关键特性包括:
- 对象(Objects): 捆绑状态(数据)和行为(方法)的实例。
- 类(Classes): 创建对象的蓝图或模板(在基于原型(prototype-based)的 Lua 中不那么直接)。
- 封装(Encapsulation): 将数据和操作数据的方法捆绑在一个单元内,并限制对对象某些组件的直接访问。
- 继承(Inheritance): 新类(或 Lua 中的对象)从现有类继承属性和方法的机制。
- 多态(Polymorphism): 对象可以呈现多种形式的能力,通常允许方法根据作用于的对象执行不同的操作。
Lua 中的 OOP:基于原型
Section titled “Lua 中的 OOP:基于原型”Lua 没有像 Java 或 C++ 那样的内置类。相反,Lua 中的 OOP 通常使用表和元表来实现,遵循基于原型的方法。对象直接从其他对象(它们的原型)继承。
- 对象即表: Lua 对象通常由一个表表示。其字段存储对象的状态,其方法是存储在表(或其原型)中的函数。
- 原型和
__index: 通过设置表的元表并使用__index元方法(metamethod)指向其原型来实现继承。如果在对象中找不到字段或方法,Lua 会通过__index在原型中查找。 self约定: 方法通常使用冒号运算符(:)定义和调用,这会隐式地(implicitly)将对象自身作为第一个参数传递,按照约定命名为self。
一个简单的类似类的结构:Rectangle
Section titled “一个简单的类似类的结构:Rectangle”让我们为 Rectangle 创建一个“类”(原型表)。
-- Rectangle.lua(我们的原型或“类”表)local Rectangle = {}Rectangle.__index = Rectangle -- 继承的关键:方法在 Rectangle 本身中查找
-- 构造函数(按照约定命名为 'new')function Rectangle:new(initial_length, initial_breadth) local o = {} -- 创建一个空的表(即实例) setmetatable(o, self) -- 将其元表设置为 Rectangle(或派生类) -- 在这里,当作为 Rectangle:new() 调用时,self 将是 Rectangle o.length = initial_length or 0 o.breadth = initial_breadth or 0 return oend
-- 计算面积的方法function Rectangle:calculateArea() return self.length * self.breadthend
-- 打印面积的方法function Rectangle:printArea() print("The area of the rectangle is: ", self:calculateArea())end
-- 要使其成为一个模块,通常会返回 Rectangle 表-- return Rectangle创建和使用对象(实例)
Section titled “创建和使用对象(实例)”要使用我们的 Rectangle“类”,首先需要确保它已加载(例如,如果它在单独的文件中,则通过 require 加载,或者仅在同一作用域(scope)内定义)。
-- 假设上面定义的 Rectangle 可用
-- 创建 Rectangle 的一个实例(对象)local rect1 = Rectangle:new(10, 5)
-- 访问属性print("Rectangle 1 Length:", rect1.length) -- Output: 10 -- 输出:10print("Rectangle 1 Breadth:", rect1.breadth) -- Output: 5 -- 输出:5
-- 调用方法rect1:printArea() -- Output: The area of the rectangle is: 50 -- 输出:矩形的面积是: 50
local rect2 = Rectangle:new(7, 3)rect2:printArea() -- Output: The area of the rectangle is: 21 -- 输出:矩形的面积是: 21self 和 : 的工作原理:
- 当你定义
function Rectangle:new(...)时,它是Rectangle.new = function(self, ...)的语法糖(syntactic sugar)。 - 当你调用
rect1:printArea()时,它是rect1.printArea(rect1)的语法糖。
继承:从 Shape 创建 Square
Section titled “继承:从 Shape 创建 Square”让我们首先定义一个更通用(generic)的 Shape 原型,然后让 Square 从它继承。
-- Shape.lua(基础原型)local Shape = {}Shape.__index = Shape
function Shape:new() local o = {} setmetatable(o, self) return oend
function Shape:getType() return "Generic Shape" -- 通用形状end
-- Square.lua(派生原型)local Square = Shape:new() -- 创建 Square 对象,最初从 Shape 继承 -- (或者 Square = setmetatable({}, Shape) 然后 Square.__index = Square 用于 Square 特有的方法)Square.__index = Square -- 实例中找不到的方法先在 Square 中查找,然后通过元表链在 Shape 中查找setmetatable(Square, Shape) -- Square 的元表是 Shape,所以 Square “是”一个 Shape 原型
-- Square 自己的构造函数function Square:new(side) local o = Shape.new(self) -- 调用 Shape 的 'new',但要确保元表设置为 Square -- 更健壮的方式:local o = {}; setmetatable(o, self) o.side = side or 0 return oend
-- 重写 Shape 的 getType 方法function Square:getType() return "Square" -- 正方形end
-- Square 特有的方法function Square:calculateArea() return self.side * self.sideend
function Square:printArea() print("The area of the square (side " .. self.side .. ") is: " .. self:calculateArea()) -- 正方形(边长为 .. self.side .. )的面积是: .. self:calculateArea()end
-- 示例用法:local generic_shape = Shape:new()print(generic_shape:getType()) -- Output: Generic Shape -- 输出:通用形状
local my_square = Square:new(7)print(my_square:getType()) -- Output: Square (overridden method) -- 输出:正方形(重写的方法)my_square:printArea() -- Output: The area of the square (side 7) is: 49 -- 输出:正方形(边长为 7)的面积是:49
-- 演示 Square 本身没有的方法的继承链:-- 如果 Square 有一个像 { __index = Shape } 这样的元表,并且 Square.getType 是 nil,-- my_square:getType() 将解析为 Shape:getType()。-- 我们当前的设置 Square.__index = Square; setmetatable(Square, Shape) 为“类”方法实现了这一点-- 并且实例方法通过 instance_mt.__index = Square 解析。解释 Square 的继承:
local Square = Shape:new()或更明确地说Square = {}。setmetatable(Square, { __index = Shape })。这使得Square继承自Shape。如果Square中没有一个键,Lua 会在Shape中查找。Square.__index = Square。对于Square的实例,如果在实例中没有一个键,Lua 会在Square表本身中查找。- 构造函数
Square:new(side):创建一个实例o,将其元表设置为Square(因此o继承自Square),并初始化其特定属性。
一个更常见的与 `Shape:new` 相关的 `Square:new` 模式:-- 更健壮的 Square 构造函数,假设 Shape 有一个构造函数 Shape:new(o_template)-- Shape.lua -- Shape.lua-- ...(之前的 Shape 原型)-- function Shape:new(o_template)-- o_template = o_template or {}-- setmetatable(o_template, self)-- self.__index = self -- 确保实例在其类原型中查找方法-- return o_template-- end
-- Square.lua -- Square.lua-- ...(Square 原型设置,Square.__index = Square,setmetatable(Square, {__index = Shape}))-- function Square:new(side)-- local o = Shape:new(self) -- 这里的 'self' 是 Square,因此 Shape:new 正确地为 Shape 部分设置了元表-- -- 然而,o 的元表现在是 Shape。我们需要它成为 Square。-- -- 一个更好的方法:-- local o = {}-- setmetatable(o, self) -- self 是 Square,因此 o 的元表指向 Square-- Shape.initialize(o) -- 如果 Shape 有一个 initialize 方法(可选)-- o.side = side or 0-- return o-- end
-- Square 继承 Rectangle(来自第一个示例)的最简单模式:-- 最初定义的 Rectangle 原型
-- Square 原型local Square = {}setmetatable(Square, {__index = Rectangle}) -- Square 继承自 RectangleSquare.__index = Square -- Square 的实例首先在 Square 中查找方法
function Square:new(side) local o = {} setmetatable(o, self) -- 当作为 Square:new() 调用时,self 是 Square 表 o.length = side -- 如果复用 Rectangle 的方法,使用 Rectangle 期望的字段 o.breadth = side -- o.side = side -- 或者使用它自己的字段 return oend
-- 可选地重写方法function Square:printArea() -- 如果使用 .side: -- print("The area of the square is: ", self.side * self.side) -- 打印("正方形的面积是:", self.side * self.side) -- 如果通过继承的字段复用 Rectangle 的 calculateArea: print("The area of the square is: ", Rectangle.calculateArea(self)) -- 打印("正方形的面积是:", Rectangle.calculateArea(self))end
local sq = Square:new(4)sq:printArea() -- 将调用 Square 的 printArea-- 如果 Rectangle 的 calculateArea 没有被覆盖,则调用它:-- print(Rectangle.calculateArea(sq)) -- 打印(Rectangle.calculateArea(sq))实际应用: Lua 中的 OOP 在游戏开发中被广泛用于角色实体、UI 元素(UI elements)和游戏系统。在大型应用程序中,它也用于将代码组织成易于管理和可重用组件(reusable components)。
深入学习: 探索 mixins、多重继承(可以通过 __index 函数模拟(emulated))以及 Lua 中不同的 OOP 实现模式(implementation patterns)等概念。Roberto Ierusalimschy 撰写的书籍《Programming in Lua》提供了关于此主题的优秀章节。