通过闭包访问
本课属于 Coddy Lua 学习旅程的“面向对象编程”单元。第 45 课,共 70 课。
如果你无法与私有局部变量交互,它们就毫无用处。解决方法是在构造函数内部定义 getter 和 setter 方法,这样它们就能访问闭包的作用域。
由于这些方法是在 :new() 内创建的,因此它们可以“看到”局部变量。你可以直接将它们赋给对象:
local Counter = {}
Counter.__index = Counter
function Counter:new()
local count = 0 -- 私有变量
local obj = {}
-- 在构造函数中定义方法
function obj:getCount()
return count
end
function obj:increment()
count = count + 1
end
setmetatable(obj, Counter)
return obj
end现在可以访问私有变量 count,但只能通过你提供的方法访问:
local c = Counter:new()
print(c:getCount()) -- 0
c:increment()
c:increment()
print(c:getCount()) -- 2
print(c.count) -- nil(仍然隐藏!)注意,:getCount() 和 :increment() 是直接定义在 obj 上的,而不是类表上。每个实例都有自己的这些函数副本,每个副本都可以访问各自私有的 count。这就是权衡:真正的私有性会多占用一些内存,因为方法不会通过原型共享。
挑战
简单让我们创建一个使用基于闭包的隐私机制来存储摄氏温度值的 Temperature 类,并在构造函数中定义 getter 和 setter 方法,以便受控地访问温度值!
你会把代码分成两个文件:
Temperature.lua:创建一个类,在构造函数内部用局部变量存储温度值,使其完全无法从外部访问。构造函数:new(initialCelsius)应将温度存储在局部变量中,然后直接在对象实例上定义以下方法::getCelsius():返回当前的摄氏温度:setCelsius(value):将温度更新为新值:getFahrenheit():使用公式(celsius * 9/5) + 32将温度转换为华氏温度并返回
main.lua:引入 Temperature 模块,并从输入中读取两个值:初始温度和要设置的新温度。使用初始值创建一个 Temperature 实例,打印摄氏温度和华氏温度,然后使用 setter 更改温度,并再次打印这两个温度读数。
你会收到两个输入:
- 初始摄氏温度(一个数字)
- 要设置的新温度(一个数字)
输出应为四行,显示温度更改前后的读数:
Celsius: {initialValue}
Fahrenheit: {convertedInitial}
Celsius: {newValue}
Fahrenheit: {convertedNew}例如,如果输入是 0 和 100,输出应为:
Celsius: 0
Fahrenheit: 32
Celsius: 100
Fahrenheit: 212请记住:温度值应存储在 :new() 内部的局部变量中,而不是存储在 self 上。getter 和 setter 方法定义在构造函数内部,因此可以通过闭包访问这个私有变量。直接访问 temp.celsius 或 temp._celsius 应返回 nil!
亲手试试
-- 加载 Temperature 模块
local Temperature = require('Temperature')
-- 读取输入值
local initialTemp = tonumber(io.read())
local newTemp = tonumber(io.read())
-- TODO:用初始值创建一个 Temperature 实例
-- TODO:打印初始的摄氏度和华氏度读数
-- TODO:使用设置方法将温度改为 newTemp
-- TODO:打印更新后的摄氏度和华氏度读数
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Lua 在线编译器