30. Designing a small class — Homework solutions
The .lua solution files are in
exercises/30/homework/solutions/.
Problem 1 — Counter class
Worked solution.
local Counter = {}
Counter.__index = Counter
function Counter.new()
return setmetatable({ count = 0 }, Counter)
end
function Counter:increment() self.count = self.count + 1 end
function Counter:get() return self.count end
function Counter:reset() self.count = 0 end
local c = Counter.new()
c:increment(); c:increment(); c:increment()
print(c:get()) -- 3
c:reset()
print(c:get()) -- 0Problem 2 — Clamped health
How to think about it. Enforce "stay between 0 and
max" inside heal and damage, so no caller can
push health out of range.
Worked solution.
local Health = {}
Health.__index = Health
function Health.new(max)
return setmetatable({ max = max, current = max }, Health)
end
function Health:heal(n)
self.current = self.current + n
if self.current > self.max then self.current = self.max end
end
function Health:damage(n)
self.current = self.current - n
if self.current < 0 then self.current = 0 end
end
function Health:get() return self.current end
local h = Health.new(100)
h:damage(30); print(h:get()) -- 70
h:heal(1000); print(h:get()) -- 100 (capped at max)
h:damage(9999); print(h:get()) -- 0 (floored at 0)Problem 3 — Light switch
Worked solution.
local Switch = {}
Switch.__index = Switch
function Switch.new()
return setmetatable({ on = false }, Switch)
end
function Switch:toggle() self.on = not self.on end
function Switch:isOn() return self.on end
local s = Switch.new()
print(s:isOn()) -- false
s:toggle(); print(s:isOn()) -- true
s:toggle(); print(s:isOn()) -- falseself.on = not self.on flips the boolean each call — the
whole switch in one line.
Challenge — Stack with a guard
Problem. :pop() on an empty stack
returns nil instead of crashing.
Worked solution.
local Stack = {}
Stack.__index = Stack
function Stack.new()
return setmetatable({ items = {} }, Stack)
end
function Stack:push(v) table.insert(self.items, v) end
function Stack:pop() return table.remove(self.items) end
function Stack:size() return #self.items end
local s = Stack.new()
s:push("a"); s:push("b")
print(s:size()) -- 2
print(s:pop()) -- b
print(s:pop()) -- a
print(s:pop()) -- nil (empty, but no crash)
print(s:size()) -- 0table.remove on an empty list returns nil
instead of erroring, so the guard is built in — but know why it
is safe.
Done?
That is the end of Part 6. You can build classes, make many, inherit, override, and design a clean, safe public surface. Part 6 has two mini-projects: the Inventory System and a Monster Battle, both built from cooperating objects.