包含关系
本课属于 Coddy Lua 学习旅程的“面向对象编程”单元。第 49 课,共 70 课。
在整个课程中,你都使用继承来共享类之间的行为:Dog“是一种”Animal。但继承并不总是合适的工具。
有时,对象之间没有继承关系,而是一个对象直接包含另一个对象。这称为组合,它表示“拥有”关系。
想想汽车。汽车不是一种发动机,而是有发动机。发动机是一个独立的对象,存在于汽车内部:
local Engine = {}
Engine.__index = Engine
function Engine:new(horsepower)
local obj = {horsepower = horsepower}
setmetatable(obj, Engine)
return obj
end
local Car = {}
Car.__index = Car
function Car:new(model, engine)
local obj = {
model = model,
engine = engine -- 将 Engine 对象存入 Car 中
}
setmetatable(obj, Car)
return obj
end创建汽车时,你要将一个现有的发动机对象传递给它的构造函数:
local myEngine = Engine:new(200)
local myCar = Car:new("Sedan", myEngine)
print(myCar.engine.horsepower) -- 200汽车不会继承发动机,而只是持有对发动机的引用。这样可以让两个类保持独立。你可以换用另一种发动机,也可以在摩托车中使用同一个发动机类。
组合能带给你继承无法提供的灵活性:你可以用各个部分组装对象,而不是把它们固定在僵化的类层次结构中。
挑战
简单让我们用组合来构建一个计算机系统!你将创建一个内部包含CPU 的 Computer,而不是使用继承:这展示了“拥有”关系。
你将把代码分成三个文件:
CPU.lua:创建一个 CPU 类,其构造函数:new(brand, speed)会保存品牌名称和以 GHz 为单位的速度。添加一个:getInfo()方法,让它以以下格式返回字符串:{brand} @ {speed}GHzComputer.lua:创建一个 Computer 类,其构造函数:new(model, cpu)接收型号名称和一个 CPU 对象。Computer 会将 CPU 对象存储在自身内部(组合!)。添加一个:describe()方法,用于输出计算机的型号和 CPU 信息。main.lua:把所有内容整合起来!读取 CPU 品牌、CPU 速度和计算机型号的输入。先创建一个 CPU 对象,再将它传给 Computer 构造函数。最后,调用计算机的:describe()方法。
你将收到三个输入:
- CPU 品牌(字符串)
- 以 GHz 为单位的 CPU 速度(数字)
- 计算机型号名称(字符串)
你的输出应显示计算机的描述,其中包含其内部 CPU 的信息:
Model: {model}
CPU: {brand} @ {speed}GHz例如,如果输入是 Intel、3.5 和 WorkStation,输出应为:
Model: WorkStation
CPU: Intel @ 3.5GHz注意,Computer 并不是从 CPU 继承而来的。它只是保存了对 CPU 的引用。Computer 的 :describe() 方法通过存储的对象访问 CPU 的数据,展示了组合如何让你用简单的部分构建复杂对象!
亲手试试
-- main.lua
-- 用组合把所有部分整合起来
local CPU = require('CPU')
local Computer = require('Computer')
-- 读取输入
local cpuBrand = io.read()
local cpuSpeed = tonumber(io.read())
local computerModel = io.read()
-- TODO:用品牌和速度创建一个 CPU 对象
-- TODO:用型号和 CPU 对象创建一个 Computer 对象
-- TODO:调用计算机的 describe() 方法,打印输出
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Lua 在线编译器