Menu
CoddyTech

包含关系

本课属于 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

汽车不会继承发动机,而只是持有对发动机的引用。这样可以让两个类保持独立。你可以换用另一种发动机,也可以在摩托车中使用同一个发动机类。

组合能带给你继承无法提供的灵活性:你可以用各个部分组装对象,而不是把它们固定在僵化的类层次结构中。

challenge icon

挑战

简单

让我们用组合来构建一个计算机系统!你将创建一个内部包含CPU 的 Computer,而不是使用继承:这展示了“拥有”关系。

你将把代码分成三个文件:

  • CPU.lua:创建一个 CPU 类,其构造函数 :new(brand, speed) 会保存品牌名称和以 GHz 为单位的速度。添加一个 :getInfo() 方法,让它以以下格式返回字符串:{brand} @ {speed}GHz
  • Computer.lua:创建一个 Computer 类,其构造函数 :new(model, cpu) 接收型号名称和一个 CPU 对象。Computer 会将 CPU 对象存储在自身内部(组合!)。添加一个 :describe() 方法,用于输出计算机的型号和 CPU 信息。
  • main.lua:把所有内容整合起来!读取 CPU 品牌、CPU 速度和计算机型号的输入。先创建一个 CPU 对象,再将它传给 Computer 构造函数。最后,调用计算机的 :describe() 方法。

你将收到三个输入:

  1. CPU 品牌(字符串)
  2. 以 GHz 为单位的 CPU 速度(数字)
  3. 计算机型号名称(字符串)

你的输出应显示计算机的描述,其中包含其内部 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() 方法,打印输出
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Lua 在线编译器