Menu
CoddyTech

Mixin đơn giản

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 51 trên 70.

Tính kết hợp cho phép bạn đặt các đối tượng bên trong đối tượng khác. Nhưng nếu bạn muốn dùng chung hành vi giữa các lớp không liên quan thì sao?

Player và MenuItem đều có thể cần phương thức :describe(), nhưng chúng không có chung lớp cha. Đây là lúc mixin phát huy tác dụng.

Mixin đơn giản là một bảng chứa các hàm. Bạn “trộn” các hàm này vào một lớp bằng cách sao chép chúng vào đó:

local Printable = {}

function Printable:describe()
    print("Object: " .. tostring(self))
end

function Printable:log(message)
    print("[LOG] " .. message)
end

Để áp dụng mixin này cho một lớp, bạn sao chép từng hàm từ bảng mixin vào bảng lớp:

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

local Player = {}
Player.__index = Player

applyMixin(Player, Printable)  -- Giờ Player có :describe() và :log()

Sau khi áp dụng mixin, mọi đối tượng Player đều có thể gọi các phương thức đó như thể chúng được định nghĩa trực tiếp trong lớp:

local p = setmetatable({}, Player)
p:log("Player created")  -- [LOG] Đã tạo Player

Khác với kế thừa, mixin không tạo ra mối quan hệ cha con. Bạn chỉ đơn giản sao chép các hàm từ bảng này sang bảng khác.

Mixin rất phù hợp để chia sẻ các hành vi tiện ích như ghi nhật ký, tuần tự hóa hoặc các phương thức mô tả giữa những lớp vốn không có điểm chung nào khác.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống gắn thẻ bằng mixin! Bạn sẽ tạo một mixin Taggable có thể áp dụng cho mọi lớp, giúp các đối tượng quản lý tập hợp thẻ, hữu ích khi phân loại mục, bài đăng hoặc bất kỳ thực thể nào cần nhãn.

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

  • Taggable.lua: Tạo một bảng mixin chứa hai phương thức:
    • :addTag(tag): thêm một thẻ vào self.tags (giả định bảng này đã tồn tại)
    • :listTags(): in ra tất cả thẻ theo định dạng Tags: tag1, tag2, tag3 (hoặc Tags: none nếu danh sách trống)
  • Article.lua: Tạo lớp Article với hàm khởi tạo :new(title), lưu tiêu đề và khởi tạo một bảng tags trống. Áp dụng mixin Taggable để các instance Article có thể gắn thẻ. Thêm phương thức :getTitle() để trả về tiêu đề bài viết.
  • main.lua: Viết hàm trợ giúp applyMixin tại đây (bạn sẽ dùng hàm này trong Article.lua thông qua require). Sau đó, đọc đầu vào gồm tiêu đề bài viết và hai thẻ. Tạo một Article, thêm cả hai thẻ vào đó, in ra tiêu đề và liệt kê tất cả thẻ.

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

  1. Tiêu đề bài viết (một chuỗi)
  2. Thẻ đầu tiên cần thêm (một chuỗi)
  3. Thẻ thứ hai cần thêm (một chuỗi)

Đầu ra cần hiển thị tiêu đề bài viết, sau đó là các thẻ:

Title: {title}
Tags: {tag1}, {tag2}

Ví dụ, nếu đầu vào là Lua Basics, programming và tutorial, đầu ra sẽ là:

Title: Lua Basics
Tags: programming, tutorial

Hãy nhớ: các phương thức mixin hoạt động vì instance Article có bảng tags để các phương thức mixin thao tác. Mixin không tạo bảng này. Hàm khởi tạo Article của bạn mới tạo bảng đó. Mixin chỉ cung cấp hành vi dùng chung!

Tự làm thử

-- Tệp chính của hệ thống gắn thẻ
local Taggable = require('Taggable')
local Article = require('Article')

-- TODO: Triển khai hàm 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
function applyMixin(target, mixin)
    -- TODO: Duyệt qua mixin và sao chép từng phương thức sang target
end

-- Đọc dữ liệu đầu vào
local title = io.read()
local tag1 = io.read()
local tag2 = io.read()

-- TODO: Tạo một Article với tiêu đề đã cho

-- TODO: Thêm cả hai thẻ vào bài viết

-- TODO: In tiêu đề theo định dạng: Title: {title}

-- TODO: Liệt kê tất cả thẻ bằng phương thức listTags
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