只读表
本课属于 Coddy Lua 学习旅程的“面向对象编程”单元。第 46 课,共 70 课。
闭包可以实现真正的私有性,但需要在构造函数中定义方法。对于另一种使用场景,还有一种做法:在创建对象后将其设为只读。如果你希望字段可见但不可更改,例如固定点的坐标,这种做法就很有用。
__newindex 元方法会拦截向表赋新值的操作。让它阻止所有赋值操作,就可以冻结一个对象:
local ImmutablePoint = {}
function ImmutablePoint.new(x, y)
local data = {x = x, y = y}
local proxy = {}
setmetatable(proxy, {
__index = data,
__newindex = function()
error("Cannot modify immutable object")
end
})
return proxy
end诀窍是使用代理表。实际数据存储在 data 中,而用户与空的 proxy 交互。
读取时,__index 会从 data 中获取值。写入时,则会触发 __newindex,我们会拦截它:
local p = ImmutablePoint.new(10, 20)
print(p.x) -- 10(可以读取)
p.x = 50 -- 错误:无法修改不可变对象这种模式适用于配置对象、数学常量,或任何初始化后不应更改的数据。对象仍然完全可读,只是你无法更改它。
挑战
简单让我们构建一个 ImmutableConfig 类,用来创建只读配置对象!创建后,这些配置对象应允许读取设置,但完全禁止任何修改尝试。
你需要将代码分成两个文件:
ImmutableConfig.lua:创建一个能生成不可变配置对象的模块。你的.new(appName, version, maxUsers)函数应在内部存储这三个值,然后返回一个代理表,允许读取所有字段,但禁止任何修改。当有人尝试更改值时,应输出Error: Config is read-only,而不是引发真正的错误。main.lua:让你的 ImmutableConfig 真正发挥作用!读取三个配置值作为输入,创建一个配置对象,然后通过以下操作展示它能正常工作:- 输出全部三个原始值
- 尝试修改
maxUsers字段(这应触发错误消息) - 再次输出
maxUsers,以证明它实际上没有改变
你会收到三个输入:
- 应用名称(字符串)
- 版本(字符串)
- 最大用户数(数字)
输出应显示配置值、尝试修改时的错误,以及该值保持不变的确认信息:
App: {appName}
Version: {version}
Max Users: {maxUsers}
Error: Config is read-only
Max Users: {maxUsers}例如,如果输入是 MyApp、2.0 和 100,输出应为:
App: MyApp
Version: 2.0
Max Users: 100
Error: Config is read-only
Max Users: 100记住:代理模式使用一个供用户操作的空表,而实际数据存放在单独的内部表中。__index 元方法会从数据表中获取值,而 __newindex 会拦截(并阻止)任何写入尝试!
亲手试试
-- 导入 ImmutableConfig 模块
local ImmutableConfig = require('ImmutableConfig')
-- 读取输入
local appName = io.read()
local version = io.read()
local maxUsers = tonumber(io.read())
-- TODO:使用 ImmutableConfig.new() 创建配置对象
-- TODO:按以下格式打印全部三个原始值:
-- App: {appName}
-- Version: {version}
-- Max Users: {maxUsers}
-- TODO:尝试修改 maxUsers 字段(这应该会触发错误消息)
-- TODO:再次打印 maxUsers,证明它没有被修改
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Lua 在线编译器