Menu
CoddyTech

应用多个混入

本课属于 Coddy Lua 学习旅程的“面向对象编程”单元。第 52 课,共 70 课。

单个混入会为类添加一组行为。但实际对象通常需要具备来自多个来源的能力。游戏对象可能既需要四处移动,又需要在屏幕上显示:这是两个不同的功能,不应该放在同一个混入中。

解决方法很简单:将多个混入应用到同一个类。每个混入都会提供自己的方法,最终这个类会拥有所有这些方法:

local Movable = {}
function Movable:move(dx, dy)
    self.x = self.x + dx
    self.y = self.y + dy
end

local Visible = {}
function Visible:show()
    print("Showing at " .. self.x .. ", " .. self.y)
end
function Visible:hide()
    print("Hidden")
end

使用之前的同一个辅助函数,将两个混入都应用到你的类中:

local function applyMixin(class, mixin)
    for key, value in pairs(mixin) do
        class[key] = value
    end
end

local GameObject = {}
GameObject.__index = GameObject

applyMixin(GameObject, Movable)
applyMixin(GameObject, Visible)

现在,GameObject 实例既可以移动,也可以显示自己:

local obj = setmetatable({x = 0, y = 0}, GameObject)
obj:move(5, 3)
obj:show()  -- 显示在 5, 3 处

注意:如果两个混入定义了同名方法,后应用的会覆盖先应用的。请让混入中的方法名称各不相同,以避免出现意外行为。

challenge icon

挑战

简单

让我们用 mixin 为游戏创建一个具备多种能力的 Character 类吧!你的角色既能攻击敌人,也能治疗自己:这两种不同的行为分别来自独立的 mixin。

你会把代码分布在 4 个文件中:

  • Attacker.lua:创建一个 mixin,其中包含一个 :attack(targetName) 方法,用于打印 {self.name} attacks {targetName} for {self.damage} damage!(假设对象具有 name 和 damage 字段)
  • Healer.lua:创建一个 mixin,其中包含一个 :heal(amount) 方法,该方法会将 amount 加到 self.health 上,并打印 {self.name} heals for {amount}. Health: {newHealth}
  • Character.lua:创建一个 Character 类,其中包含构造函数 :new(name, health, damage),用于存储这 3 个属性。应用 Attacker 和 Healer 两个 mixin,让每个角色都能攻击和治疗。添加一个 :status() 方法,用于打印 {name} - Health: {health}, Damage: {damage}
  • main.lua:在这里编写 applyMixin 辅助函数。读取角色的名称、生命值、伤害值、攻击目标和治疗量。创建一个 Character,显示其状态,让它攻击目标、治疗自己,然后再次显示其状态。

你会收到 5 个输入:

  1. 角色名称(字符串)
  2. 初始生命值(数字)
  3. 伤害值(数字)
  4. 攻击目标的名称(字符串)
  5. 治疗量(数字)

输出应显示角色使用了两个 mixin 的能力:

{name} - Health: {health}, Damage: {damage}
{name} attacks {target} for {damage} damage!
{name} heals for {healAmount}. Health: {newHealth}
{name} - Health: {newHealth}, Damage: {damage}

例如,如果输入是 Warrior、100、25、Goblin 和 30,输出应为:

Warrior - Health: 100, Damage: 25
Warrior attacks Goblin for 25 damage!
Warrior heals for 30. Health: 130
Warrior - Health: 130, Damage: 25

每个 mixin 都会为 Character 类提供自己的方法。角色不会继承任何一个 mixin,而是通过 applyMixin 函数获得它们的能力。这就是组合多个 mixin 的优势!

亲手试试

-- 主文件

-- TODO: 编写 applyMixin 辅助函数
-- 这个函数应将 mixin 表中的所有方法复制到目标表中
local function applyMixin(target, mixin)
    -- TODO: 实现 mixin 的应用
end

-- 让 applyMixin 在全局范围内可用,以便 Character.lua 使用
_G.applyMixin = applyMixin

local Character = require('Character')

-- 读取输入
local name = io.read()
local health = tonumber(io.read())
local damage = tonumber(io.read())
local targetName = io.read()
local healAmount = tonumber(io.read())

-- TODO: 用给定的 name、health 和 damage 创建一个 Character

-- TODO: 显示角色的状态

-- TODO: 让角色攻击目标

-- TODO: 让角色治疗自己

-- TODO: 再次显示角色的状态
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: Lua 在线编译器