委托
本课属于 Coddy Lua 学习旅程的“面向对象编程”单元。第 50 课,共 70 课。
将一个对象存储在另一个对象中只是第一步。组合真正的强大之处在于委托,也就是外部对象将方法调用转发给其内部组件。
想一想启动汽车的过程。你不会伸到引擎盖下面手动转动发动机,而是转动钥匙,汽车会在内部完成剩下的工作。
在代码中,这意味着 Car 类提供了一个简单的 :start() 方法,并将实际工作委托给引擎:
function Car:start()
print("Turning the key...")
self.engine:run() -- 交给内部对象处理
end汽车不需要知道发动机是如何运行的。它只需告诉发动机做好自己的工作。这样可以分离各自的职责。Engine 类负责处理发动机逻辑,而 Car 负责协调各个部分:
function Engine:run()
print("Engine running at " .. self.horsepower .. " HP")
end
local engine = Engine:new(250)
local car = Car:new("Coupe", engine)
car:start()
-- 输出:
-- 正在转动钥匙……
-- Engine 正以 250 HP 运行委托为类的使用者提供了清晰的接口。他们与汽车交互,而不是与汽车的内部部件交互。如果你之后换用不同的引擎实现,汽车的公共接口仍然保持不变:只有内部的委托会改变。
挑战
简单让我们构建一个展示委托的音乐播放器系统!你将创建一个 Speaker 组件,以及一个包含它的 MusicPlayer。播放器不会直接处理声音输出,而是把这项工作委托给内部的扬声器。
你将把代码分别写在三个文件中:
Speaker.lua:创建一个 Speaker 类,其构造函数:new(volume)用于保存音量。添加一个:playSound(trackName)方法,用于打印:Playing "{trackName}" at volume {volume}MusicPlayer.lua:创建一个 MusicPlayer 类,其构造函数:new(brand, speaker)接收品牌名称和一个 Speaker 对象。播放器会将扬声器保存在自身内部。添加以下方法::play(trackName):打印{brand} player starting...,然后将工作委托给扬声器的:playSound()方法:stop():打印{brand} player stopped
main.lua:把所有部分组合起来!读取扬声器音量、播放器品牌和曲目名称。先创建一个 Speaker,再将它传给 MusicPlayer 构造函数。调用播放器的:play()方法并传入曲目名称,然后调用:stop()。
你会收到 3 个输入:
- 扬声器音量(一个数字)
- 音乐播放器品牌(一个字符串)
- 要播放的曲目名称(一个字符串)
输出应显示播放器如何与扬声器协作:
{brand} player starting...
Playing "{trackName}" at volume {volume}
{brand} player stopped例如,如果输入是 75、SoundMax 和 Summer Vibes,输出应为:
SoundMax player starting...
Playing "Summer Vibes" at volume 75
SoundMax player stopped注意,MusicPlayer 并不知道如何发出声音。它只是让扬声器处理这部分工作。这就是委托的实际应用:外部对象提供简洁的接口,内部组件则负责专门的工作!
亲手试试
-- main.lua
-- 把所有部分组合起来!
local Speaker = require('Speaker')
local MusicPlayer = require('MusicPlayer')
-- 读取输入
local volume = tonumber(io.read())
local brand = io.read()
local trackName = io.read()
-- TODO:用给定的音量创建一个 Speaker
-- TODO:用品牌和扬声器创建一个 MusicPlayer
-- TODO:用曲目名称调用播放器的 play 方法
-- TODO:调用播放器的 stop 方法
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Lua 在线编译器