Menu
CoddyTech

Áp dụng nhiều mixin

Thuộc phần Lập trình hướng đối tượng trong hành trình Lua của Coddy. Bài học 52 trên 70.

Một mixin thêm một nhóm hành vi vào lớp. Nhưng các đối tượng thực tế thường cần khả năng từ nhiều nguồn. Một đối tượng trong trò chơi có thể cần di chuyển xung quanh và hiển thị trên màn hình: hai mối quan tâm riêng biệt không thuộc cùng một mixin.

Giải pháp rất đơn giản: áp dụng nhiều mixin cho cùng một lớp. Mỗi mixin đóng góp các phương thức riêng, và lớp đó sẽ có tất cả các phương thức này:

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

Áp dụng cả hai mixin cho lớp của bạn bằng cùng hàm trợ giúp đã dùng trước đó:

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)

Giờ đây, các đối tượng GameObject có thể vừa di chuyển vừa hiển thị chính mình:

local obj = setmetatable({x = 0, y = 0}, GameObject)
obj:move(5, 3)
obj:show()  -- Hiển thị tại 5, 3

Lưu ý: nếu hai mixin định nghĩa phương thức trùng tên, mixin được áp dụng sau sẽ ghi đè mixin đầu tiên. Hãy đặt tên các phương thức trong mixin khác nhau để tránh hành vi ngoài dự kiến.

challenge icon

Thử thách

Dễ

Hãy xây dựng một lớp Character cho trò chơi, kết hợp nhiều khả năng bằng mixin! Nhân vật của bạn có thể vừa tấn công kẻ địch vừa tự hồi máu: hai hành vi riêng biệt đến từ hai mixin khác nhau.

Bạn sẽ sắp xếp code trong bốn tệp:

  • Attacker.lua: Tạo một mixin có phương thức :attack(targetName) để in ra {self.name} attacks {targetName} for {self.damage} damage! (giả sử đối tượng có các trường name và damage)
  • Healer.lua: Tạo một mixin có phương thức :heal(amount) để cộng lượng hồi máu vào self.health và in ra {self.name} heals for {amount}. Health: {newHealth}
  • Character.lua: Tạo một lớp Character với hàm khởi tạo :new(name, health, damage) để lưu cả ba thuộc tính. Áp dụng cả hai mixin Attacker và Healer để mọi nhân vật đều có thể tấn công và hồi máu. Thêm phương thức :status() để in ra {name} - Health: {health}, Damage: {damage}
  • main.lua: Viết hàm trợ giúp applyMixin tại đây. Đọc đầu vào gồm tên nhân vật, máu, sát thương, mục tiêu cần tấn công và lượng máu cần hồi. Tạo một Character, hiển thị trạng thái, cho nhân vật tấn công mục tiêu, tự hồi máu, rồi hiển thị lại trạng thái.

Bạn sẽ nhận năm đầu vào:

  1. Tên nhân vật (một chuỗi)
  2. Lượng máu ban đầu (một số)
  3. Giá trị sát thương (một số)
  4. Tên mục tiêu cần tấn công (một chuỗi)
  5. Lượng máu cần hồi (một số)

Đầu ra cần cho thấy nhân vật sử dụng cả hai khả năng từ mixin:

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

Ví dụ, nếu đầu vào là Warrior, 100, 25, Goblin và 30, đầu ra sẽ là:

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

Mỗi mixin bổ sung phương thức riêng vào lớp Character. Nhân vật không kế thừa từ mixin nào. Thay vào đó, nhân vật có được các khả năng của chúng thông qua hàm applyMixin. Đó chính là sức mạnh của việc kết hợp nhiều mixin!

Tự làm thử

-- Tệp chính

-- TODO: Viết hàm hỗ trợ applyMixin
-- Hàm này cần sao chép tất cả phương thức từ bảng mixin sang bảng đích
local function applyMixin(target, mixin)
    -- TODO: Triển khai việc áp dụng mixin
end

-- Đưa applyMixin vào phạm vi toàn cục để Character.lua có thể sử dụng
_G.applyMixin = applyMixin

local Character = require('Character')

-- Đọc dữ liệu đầu vào
local name = io.read()
local health = tonumber(io.read())
local damage = tonumber(io.read())
local targetName = io.read()
local healAmount = tonumber(io.read())

-- TODO: Tạo một Character với tên, máu và sát thương đã cho

-- TODO: Hiển thị trạng thái của nhân vật

-- TODO: Cho nhân vật tấn công mục tiêu

-- TODO: Cho nhân vật tự hồi máu

-- TODO: Hiển thị lại trạng thái của nhân vật
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Lua online