Bảng chỉ đọc
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 46 trên 70.
Closure mang lại tính riêng tư thực sự, nhưng bạn phải định nghĩa các phương thức bên trong hàm khởi tạo. Có một cách khác phù hợp với trường hợp sử dụng khác: tạo một đối tượng chỉ đọc sau khi khởi tạo. Cách này hữu ích khi bạn muốn các trường hiển thị nhưng không thể thay đổi, chẳng hạn như tọa độ của một điểm cố định.
Phương thức biến đổi __newindex chặn các lần thử gán giá trị mới cho bảng. Bạn có thể đóng băng một đối tượng bằng cách khiến phương thức này chặn mọi phép gán:
local ImmutablePoint = {}
function ImmutablePoint.new(x, y)
local data = {x = x, y = y}
local proxy = {}
setmetatable(proxy, {
__index = data,
__newindex = function()
error("Cannot modify immutable object")
end
})
return proxy
endMẹo ở đây là dùng một bảng proxy. Dữ liệu thực tế nằm trong data, còn người dùng tương tác với proxy trống.
Khi đọc, __index lấy dữ liệu từ data. Khi ghi, __newindex được kích hoạt thay vào đó, và chúng ta chặn thao tác này:
local p = ImmutablePoint.new(10, 20)
print(p.x) -- 10 (đọc được)
p.x = 50 -- Lỗi: Không thể sửa đổi đối tượng bất biếnMẫu này hữu ích cho các đối tượng cấu hình, hằng số toán học hoặc mọi dữ liệu không bao giờ được thay đổi sau khi khởi tạo. Bạn vẫn có thể đọc toàn bộ đối tượng. Bạn chỉ không thể thay đổi nó.
Thử thách
DễHãy tạo một lớp ImmutableConfig để tạo các đối tượng cấu hình chỉ đọc! Sau khi được tạo, các đối tượng cấu hình này cho phép đọc thiết lập nhưng chặn hoàn toàn mọi nỗ lực sửa đổi chúng.
Bạn sẽ tổ chức code trong hai tệp:
ImmutableConfig.lua: Tạo một module tạo ra các đối tượng cấu hình bất biến. Hàm.new(appName, version, maxUsers)của bạn sẽ lưu trữ ba giá trị này ở bên trong, sau đó trả về một bảng proxy cho phép đọc tất cả các trường nhưng ngăn mọi sửa đổi. Khi có người cố thay đổi một giá trị, hãy in raError: Config is read-onlythay vì gây ra lỗi thực sự.main.lua: Hãy vận dụng ImmutableConfig! Đọc ba đầu vào cho các giá trị cấu hình, tạo một đối tượng cấu hình, sau đó minh họa rằng nó hoạt động chính xác bằng cách:- In ra cả ba giá trị ban đầu
- Thử sửa đổi trường
maxUsers(việc này sẽ kích hoạt thông báo lỗi) - In ra
maxUserslần nữa để chứng minh giá trị thực sự không thay đổi
Bạn sẽ nhận ba đầu vào:
- Tên ứng dụng (một chuỗi)
- Phiên bản (một chuỗi)
- Số người dùng tối đa (một số)
Đầu ra cần hiển thị các giá trị cấu hình, lỗi khi có người thử sửa đổi và xác nhận rằng giá trị vẫn không thay đổi:
App: {appName}
Version: {version}
Max Users: {maxUsers}
Error: Config is read-only
Max Users: {maxUsers}Ví dụ, nếu đầu vào là MyApp, 2.0 và 100, đầu ra sẽ là:
App: MyApp
Version: 2.0
Max Users: 100
Error: Config is read-only
Max Users: 100Hãy nhớ: mẫu proxy sử dụng một bảng rỗng để người dùng tương tác, còn dữ liệu thực nằm trong một bảng nội bộ riêng. Metamethod __index lấy dữ liệu từ bảng dữ liệu, còn __newindex chặn mọi nỗ lực ghi!
Tự làm thử
-- Nhập module ImmutableConfig
local ImmutableConfig = require('ImmutableConfig')
-- Đọc các giá trị đầu vào
local appName = io.read()
local version = io.read()
local maxUsers = tonumber(io.read())
-- TODO: Tạo đối tượng config bằng ImmutableConfig.new()
-- TODO: In ra cả ba giá trị ban đầu theo định dạng:
-- Ứng dụng: {appName}
-- Phiên bản: {version}
-- Số người dùng tối đa: {maxUsers}
-- TODO: Thử thay đổi thuộc tính maxUsers (thao tác này sẽ gây ra thông báo lỗi)
-- TODO: In lại maxUsers để chứng minh giá trị này không thay đổi
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