Menu
CoddyTech

Truy cập qua closure

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 45 trên 70.

Biến cục bộ riêng tư sẽ vô dụng nếu bạn không thể tương tác với chúng. Cách giải quyết là định nghĩa các phương thức getter và setter bên trong hàm khởi tạo, nơi chúng có thể truy cập phạm vi của closure.

Vì các phương thức này được tạo bên trong :new(), chúng có thể “nhìn thấy” các biến cục bộ. Bạn gán chúng trực tiếp cho đối tượng:

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- Biến riêng tư
    
    local obj = {}
    
    -- Định nghĩa các phương thức bên trong hàm khởi tạo
    function obj:getCount()
        return count
    end
    
    function obj:increment()
        count = count + 1
    end
    
    setmetatable(obj, Counter)
    return obj
end

Giờ đây, bạn có thể truy cập biến count riêng tư, nhưng chỉ thông qua các phương thức bạn cung cấp:

local c = Counter:new()
print(c:getCount())  -- 0
c:increment()
c:increment()
print(c:getCount())  -- 2
print(c.count)       -- nil (vẫn bị ẩn!)

Lưu ý rằng :getCount() và :increment() được định nghĩa trực tiếp trên obj, không phải trên bảng lớp. Mỗi đối tượng có bản sao riêng của các hàm này, mỗi hàm đều có quyền truy cập vào count riêng tư của đối tượng đó. Đây là sự đánh đổi: tính riêng tư thực sự tốn thêm một chút bộ nhớ vì các phương thức không được chia sẻ thông qua nguyên mẫu.

challenge icon

Thử thách

Dễ

Hãy cùng tạo một lớp Temperature dùng tính riêng tư dựa trên closure để lưu giá trị nhiệt độ theo độ Celsius, với các phương thức getter và setter được định nghĩa bên trong hàm khởi tạo để kiểm soát quyền truy cập!

Bạn sẽ tổ chức code trong hai tệp:

  • Temperature.lua: Tạo một lớp lưu giá trị nhiệt độ trong một biến cục bộ bên trong hàm khởi tạo, hoàn toàn ẩn khỏi bên ngoài. Hàm khởi tạo :new(initialCelsius) cần lưu nhiệt độ trong một biến cục bộ, sau đó định nghĩa trực tiếp các phương thức sau trên instance:
    • :getCelsius(): trả về nhiệt độ hiện tại theo độ Celsius
    • :setCelsius(value): cập nhật nhiệt độ thành một giá trị mới
    • :getFahrenheit(): trả về nhiệt độ đã chuyển sang độ Fahrenheit theo công thức: (celsius * 9/5) + 32
  • main.lua: Nạp module Temperature và đọc hai giá trị từ đầu vào: nhiệt độ ban đầu và nhiệt độ mới cần đặt. Tạo một instance Temperature với giá trị ban đầu, in ra số đo theo độ Celsius và Fahrenheit, sau đó dùng setter để thay đổi nhiệt độ rồi in lại cả hai số đo.

Bạn sẽ nhận được hai đầu vào:

  1. Nhiệt độ ban đầu theo độ Celsius (một số)
  2. Nhiệt độ mới cần đặt (một số)

Đầu ra gồm bốn dòng hiển thị số đo nhiệt độ trước và sau khi thay đổi:

Celsius: {initialValue}
Fahrenheit: {convertedInitial}
Celsius: {newValue}
Fahrenheit: {convertedNew}

Ví dụ, nếu đầu vào là 0 và 100, đầu ra sẽ là:

Celsius: 0
Fahrenheit: 32
Celsius: 100
Fahrenheit: 212

Hãy nhớ: giá trị nhiệt độ phải được lưu trong một biến cục bộ bên trong :new(), không phải trên self. Các phương thức getter và setter được định nghĩa bên trong hàm khởi tạo, nơi chúng có thể truy cập biến riêng tư này thông qua closure. Nếu thử truy cập trực tiếp temp.celsius hoặc temp._celsius, kết quả sẽ là nil!

Tự làm thử

-- Nạp module Temperature bằng require
local Temperature = require('Temperature')

-- Đọc các giá trị đầu vào
local initialTemp = tonumber(io.read())
local newTemp = tonumber(io.read())

-- TODO: Tạo một instance Temperature với giá trị ban đầu

-- TODO: In ra số đo Celsius và Fahrenheit ban đầu

-- TODO: Dùng phương thức setter để đổi nhiệt độ thành newTemp

-- TODO: In ra số đo Celsius và Fahrenheit sau khi cập nhật
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