鸭子类型
本课属于 Coddy Lua 学习旅程的“面向对象编程”单元。第 39 课,共 70 课。
Lua 不在乎对象的类。它只关心对象能做什么。这个概念叫作鸭子类型:“如果它走起来像鸭子,叫起来也像鸭子,那它就是鸭子。”用编程术语来说,只要对象有你正在调用的方法,它就能正常工作。
这意味着完全不相关的类也可以一起使用,只要它们共享相同的方法名:
local Circle = {}
Circle.__index = Circle
function Circle:new()
local obj = {}
setmetatable(obj, Circle)
return obj
end
function Circle:draw()
print("Drawing a circle")
end
local Square = {}
Square.__index = Square
function Square:new()
local obj = {}
setmetatable(obj, Square)
return obj
end
function Square:draw()
print("Drawing a square")
end这些类之间没有继承关系,它们完全独立。不过,我们可以用相同的方式处理它们:
local shapes = {Circle:new(), Square:new(), Circle:new()}
for _, shape in ipairs(shapes) do
shape:draw()
end
-- 输出:
-- 正在绘制一个圆形
-- 正在绘制一个正方形
-- 正在绘制一个圆形循环不会检查每个对象的类型。它只调用 :draw(),并相信这个方法存在。这种灵活性很强。你可以添加新的形状类型,而完全不必修改循环代码。只要新类实现了 :draw(),就能正常工作。
挑战
简单让我们通过构建一个通知系统来展示鸭子类型的强大之处:系统中的类彼此完全无关,却都能用相同的方式处理!
你将创建三个相互独立的通知类,它们之间没有继承关系,但都实现了 :send() 方法。这意味着你可以遍历一个混合集合,并对其中每个对象调用 :send(),而无需关心它的实际类型。
你将把代码分散到四个文件中:
Email.lua:一个类,包含用于保存邮件收件人的:new(recipient)构造函数。它的:send()方法应输出Emailing {recipient}。SMS.lua:一个完全独立的类,包含:new(phoneNumber)构造函数。它的:send()方法应输出Texting {phoneNumber}。PushNotification.lua:另一个独立的类,包含:new(deviceId)构造函数。它的:send()方法应输出Pushing to {deviceId}。main.lua:把所有内容组合起来!读取三个输入,为每种通知类型各创建一个实例,把它们放进同一个表中,然后遍历表并对每个实例调用:send()。
你会收到三个输入:
- 邮件收件人(例如,
alice@example.com) - 电话号码(例如,
555-1234) - 设备 ID(例如,
device_99)
在主文件中,创建三个通知对象,把它们存入一个表中,然后遍历这个表,对每个对象调用 :send()。顺序应为:先 Email,然后 SMS,最后 PushNotification。
例如,如果输入是 bob@mail.com、555-9876 和 phone_42,输出应为:
Emailing bob@mail.com
Texting 555-9876
Pushing to phone_42注意,你的循环不需要检查每个对象的类型。它只需相信集合中的每个对象都有 :send() 方法。这就是鸭子类型的实际应用!只要它能 :send(),它就是一种通知。
亲手试试
-- main.lua
-- 汇总所有通知类型,演示鸭子类型
-- 导入通知类
local Email = require('Email')
local SMS = require('SMS')
local PushNotification = require('PushNotification')
-- 读取输入
local recipient = io.read()
local phoneNumber = io.read()
local deviceId = io.read()
-- TODO: 为每种通知类型创建一个实例
-- TODO: 把所有通知放进同一个表(先是 Email,然后是 SMS,最后是 PushNotification)
-- TODO: 遍历表,对每条通知调用 :send()
-- 这演示了鸭子类型:我们不检查类型,只调用 :send()
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Lua 在线编译器