Menu
CoddyTech

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
end

Mẹ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ến

Mẫ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ó.

challenge icon

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 ra Error: Config is read-only thay 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 maxUsers lần nữa để chứng minh giá trị thực sự không thay đổi

Bạn sẽ nhận ba đầu vào:

  1. Tên ứng dụng (một chuỗi)
  2. Phiên bản (một chuỗi)
  3. 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: 100

Hã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
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