Cộng các đối tượng
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 26 trên 70.
Bạn đã thấy các metamethod như __tostring và __eq cho phép bạn tùy chỉnh cách các đối tượng hoạt động. Bây giờ, mình sẽ mở rộng điều này sang các phép toán số học, bắt đầu với phép cộng.
Metamethod __add xác định điều gì xảy ra khi bạn dùng toán tử + giữa hai đối tượng. Khi Lua gặp a + b, Lua tìm __add trong metatable của một trong hai toán hạng.
local Vector = {}
Vector.__index = Vector
function Vector:new(x, y)
local obj = {x = x, y = y}
setmetatable(obj, Vector)
return obj
end
function Vector.__add(v1, v2)
return Vector:new(v1.x + v2.x, v1.y + v2.y)
endLưu ý rằng __add được định nghĩa bằng dấu chấm. Lua truyền cả hai toán hạng dưới dạng đối số thông thường, còn dạng dấu chấm đặt tên cho cả hai đối số. Dấu hai chấm cũng dùng được, vì function Vector:__add(other) tương đương với function Vector.__add(self, other), nhưng dạng dấu chấm là quy ước. __add nên trả về một đối tượng mới thay vì sửa đổi một trong hai đối tượng ban đầu. Nhờ vậy, phép toán luôn dễ đoán và an toàn.
local a = Vector:new(3, 4)
local b = Vector:new(1, 2)
local c = a + b
print(c.x, c.y) -- Đầu ra: 4 6Điểm mấu chốt là __add phải nằm trong metatable (bảng siêu dữ liệu) itself, ở đây là Vector, chứ không chỉ nằm trong đối tượng. Vì ta đặt Vector làm metatable cho mọi instance, tất cả chúng tự động chia sẻ hành vi này.
Thử thách
DễHãy xây dựng một lớp Vector hỗ trợ phép cộng bằng toán tử +. Đây là bước đầu tiên để bạn tìm hiểu về việc nạp chồng toán tử, giúp các đối tượng hoạt động tự nhiên với những toán tử có sẵn trong Lua.
Bạn sẽ sắp xếp code trong hai tệp:
Vector.lua: Định nghĩa lớp Vector với các thành phầnxvày. Thêm hàm khởi tạo:new(x, y)và triển khai metamethod__addđể phép cộng hai vector tạo ra một vector mới có các thành phần là tổng tương ứng. Định nghĩa__addbằng dấu chấm để cả hai toán hạng được truyền vào dưới dạng đối số có tên.main.lua: Yêu cầu module Vector, tạo các instance vector và minh họa rằng phép cộng chúng cho ra kết quả chính xác.
Bạn sẽ nhận bốn đầu vào:
- Thành phần x của vector thứ nhất
- Thành phần y của vector thứ nhất
- Thành phần x của vector thứ hai
- Thành phần y của vector thứ hai
Trong tệp chính, hãy tạo hai vector với các thành phần đã cho, cộng chúng bằng toán tử + và in các giá trị x và y của vector kết quả trên các dòng riêng biệt.
Ví dụ, nếu đầu vào là 3, 4, 1, 2, đầu ra sẽ là:
4
6Vector thứ nhất (3, 4) cộng với vector thứ hai (1, 2) bằng vector mới (4, 6).
Tự làm thử
-- Nạp module Vector bằng require
local Vector = require('Vector')
-- Đọc dữ liệu đầu vào
local x1 = tonumber(io.read())
local y1 = tonumber(io.read())
local x2 = tonumber(io.read())
local y2 = tonumber(io.read())
-- TODO: Tạo hai instance Vector từ các giá trị đầu vào
-- TODO: Cộng hai vectơ bằng toán tử +
-- TODO: In các giá trị x và y của vectơ kết quả trên hai dòng riêng
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