Liên kết bằng __index
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 7 trên 70.
Bạn có một bảng nguyên mẫu với các thuộc tính và phương thức, nhưng làm thế nào để một đối tượng mới thực sự sử dụng bảng đó? Câu trả lời nằm ở phương thức siêu dữ liệu __index.
Khi truy cập một khóa không tồn tại trong bảng, Lua thường trả về nil. Tuy nhiên, nếu bảng có một siêu bảng với trường __index, Lua sẽ tìm ở đó. Bằng cách đặt __index trỏ đến nguyên mẫu, mọi khóa còn thiếu sẽ tự động được tìm thấy trong nguyên mẫu:
local Dog = {
species = "Canis familiaris",
legs = 4
}
function Dog:bark()
print("Woof!")
end
-- Tạo một instance rỗng
local myDog = {}
-- Liên kết instance đó với nguyên mẫu
setmetatable(myDog, { __index = Dog })
print(myDog.species) -- Đầu ra: Canis familiaris
myDog:bark() -- Đầu ra: Woof!Bảng myDog hoàn toàn trống, nhưng việc truy cập myDog.species vẫn hoạt động. Khi Lua không tìm thấy species trong myDog, Lua kiểm tra __index của metatable và tìm thấy nó trong Dog.
Đây là nền tảng của lập trình hướng đối tượng trong Lua. Đối tượng có thể có dữ liệu riêng, còn hành vi dùng chung nằm trong nguyên mẫu. Trong bài học tiếp theo, bạn sẽ học cách gói mẫu này vào một hàm khởi tạo gọn gàng.
Thử thách
DễBây giờ, hãy áp dụng metamethod __index vào thực tế! Bạn sẽ tạo một prototype và liên kết một instance với nó, nhờ đó instance có thể truy cập các thuộc tính và phương thức mà bản thân nó không có trực tiếp.
Bạn sẽ sắp xếp code trong hai tệp:
Robot.lua: Định nghĩa một bảng prototypeRobotvới:- Trường
modelcó giá trị"RX-100" - Trường
batteryLevelcó giá trị100 - Phương thức
:status()in ra trạng thái hiện tại của robot
- Trường
main.lua: Yêu cầu module Robot, sau đó tạo một bảng instance rỗng và liên kết nó với prototype Robot bằngsetmetatablecùng với__index. Nhờ vậy, instance rỗng có thể truy cập mọi thứ từ prototype.
Bạn sẽ nhận một đầu vào: tên để gán cho instance robot.
Sau khi liên kết instance với prototype:
- Gán tên đầu vào cho trường
namecủa instance (trường này là riêng của instance) - Gọi phương thức
:status()trên instance
Phương thức :status() cần in ra theo đúng định dạng sau:
[name] ([model]) - Battery: [batteryLevel]%Ví dụ, nếu đầu vào là Sparky, đầu ra sẽ là:
Sparky (RX-100) - Battery: 100%Lưu ý rằng instance có name riêng, nhưng truy cập model và batteryLevel từ prototype thông qua liên kết __index.
Tự làm thử
-- Đọc đầu vào
local name = io.read()
-- Nạp module Robot bằng require
local Robot = require('Robot')
-- TODO: Tạo một bảng instance rỗng
-- TODO: Liên kết instance với prototype Robot bằng setmetatable và __index
-- TODO: Gán tên đầu vào cho trường name của instance
-- TODO: Gọi phương thức (method) :status() trên instance
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ạngTự luyện tập: Trình biên dịch Lua online