Menu
CoddyTech

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.

challenge icon

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 prototype Robot với:
    • Trường model có giá trị "RX-100"
    • Trường batteryLevel có giá trị 100
    • Phương thức :status() in ra trạng thái hiện tại của robot
    Trả về bảng ở cuối tệp.
  • 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ằng setmetatable cù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:

  1. Gán tên đầu vào cho trường name của instance (trường này là riêng của instance)
  2. 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
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