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 PlayerKhá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.
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àoself.tags(giả định bảng này đã tồn tại):listTags(): in ra tất cả thẻ theo định dạngTags: tag1, tag2, tag3(hoặcTags: nonenế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ảngtagstrố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úpapplyMixintạ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:
- Tiêu đề bài viết (một chuỗi)
- Thẻ đầu tiên cần thêm (một chuỗi)
- 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, tutorialHã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
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
1Khái niệm “self”
Bảng với hàm“self” tường minhCú pháp dấu hai chấmDấu chấm và dấu hai chấmÔn tập: Điểm chuyển động4Dự án: Ngân hàng số
Thiết lập dự ánPhương thức nạp tiền2Mẫu Prototype cho lớp
Khái niệm PrototypeLiên kết bằng __indexHàm khởi tạo :new()Khởi tạo thuộc tínhCác instance độc lậpÔn tập: Nhà máy ô tô5Nạp chồng toán tử trong OOP
Cộng các đối tượngTrừ các đối tượngNối các đối tượngSo sánh các đối tượng (<, >)Ôn tập: phép tính với ví tiền8Đóng gói
Quy ước đặt tênClosure để bảo mậtTruy cập qua closureBảng chỉ đọcLogic kiểm tra hợp lệTổng kết: Két an toàn11Mẫu thiết kế (cơ bản)
Hàm tạo đối tượngBảng SingletonMẫu IteratorObserver (Listener)Ôn tập: Factory Logger3Trạng thái và hành vi của đối tượng
Biến instancePhương thức getterPhương thức setterThuộc tính tính toánĐịnh dạng chuỗiKiểm tra tính bằng nhauÔn tập: điểm số học sinh6Cơ bản về kế thừa
Thiết lập kế thừaKế thừa phương thứcMở rộng hàm khởi tạoThêm phương thức cho lớp conDùng chung và riêngÔn tập: hệ phân cấp hình dạng9Kết hợp và mixin
Quan hệ có-mộtỦy quyềnMixin đơn giảnÁp dụng nhiều mixinMixin và kế thừaÔn tập: Lắp ráp robotTự luyện tập: Trình biên dịch Lua online