Lua Encapsulation with Closures
Encapsulation is a way to keep some state private inside an object while offering public methods to use it.
A private value is not a field on the object
The counter factory keeps count as a local value. The returned table offers add and get methods, but it has no count field.
local function newCounter()
local count = 0
local t = {}
function t.add()
count = count + 1
end
function t.get()
return count
end
return t
end
local c = newCounter()
c.add()
c.add()
print(c.get())
print(c.count)Each factory call makes fresh private state
Two counters come from the same factory. Adding to the first counter leaves the second counter at zero.
local function newCounter()
local count = 0
local t = {}
function t.add()
count = count + 1
end
function t.get()
return count
end
return t
end
local a = newCounter()
local b = newCounter()
a.add()
a.add()
print(a.get())
print(b.get())A public method controls changes to private state
The account factory keeps balance as a local value. Deposit is the only way to raise it in this program, and balance is not a field.
local function newAccount()
local balance = 100
local t = {}
function t.deposit(amount)
balance = balance + amount
end
function t.getBalance()
return balance
end
return t
end
local a = newAccount()
a.deposit(50)
print(a.getBalance())
print(a.balance)Example
local function newAccount(start)
local balance = start
local t = {}
function t.deposit(amount)
balance = balance + amount
end
function t.withdraw(amount)
if amount <= balance then
balance = balance - amount
end
end
function t.balance()
return balance
end
return t
end
local a = newAccount(100)
local b = newAccount(50)
a.deposit(25)
a.withdraw(40)
b.withdraw(10)
print(a.balance())
print(b.balance())Key points
- Private state: Private state is a local value inside a factory function that outside code cannot reach as a field.
- Public method: A public method is a function in the returned table that outside code can call.
- Factory function: A factory function returns a table of methods, and those methods keep using the factory locals after the factory returns.
- Fresh state: Each call to the factory makes fresh locals, so two objects do not share private state.
- Getter method: A getter method returns a private value without letting outside code change it as a field.
In this example, newAccount keeps balance as a local value and returns deposit, withdraw, and balance methods. Two accounts are made with different starting balances. The first account gains twenty-five and then loses forty, while the second account loses ten. The final balances are eighty-five and forty, and the balance value itself is never stored as a field on either account.
Note
Lua has no private keyword, so privacy comes from locals that are simply not placed in the returned table.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
Why does print(c.count) show nil for a counter made by the factory in this lesson?