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
endGiờ đâ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.
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:
- Nhiệt độ ban đầu theo độ Celsius (một số)
- 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: 212Hã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
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