28. Many objects together

Chapters 26 and 27 built one object at a time. Classes shine with many objects of one kind treated as a group: a party of characters, a swarm of enemies. This chapter combines two things you know — a class (Chapter 27) and a list (Chapter 22) — into the everyday pattern of a list of objects.

A class to make many of

A small Critter class — a constructor and two methods, the Chapter 27 pattern.

local Critter = {}
Critter.__index = Critter

function Critter.new(name, energy)
    local self = setmetatable({}, Critter)
    self.name = name
    self.energy = energy
    return self
end

function Critter:play()
    self.energy = self.energy - 1
    print(self.name .. " plays. Energy: " .. self.energy)
end

function Critter:isTired()
    return self.energy <= 0
end

Put the objects in a list

Each Critter.new(...) returns a fresh instance. Store several in a list, like any value:

local critters = {
    Critter.new("Spark", 3),
    Critter.new("Bolt", 2),
    Critter.new("Nova", 1),
}

critters is a normal list (Chapter 22). What's new is that each item is an object with its own state and methods.

Loop and call a method on each

Walk the list with ipairs and call a method on each object. Each acts on its own data:

for _, c in ipairs(critters) do
    c:play()
end

Output:

Spark plays. Energy: 2
Bolt plays. Energy: 1
Nova plays. Energy: 0

One loop, three objects, three separate energies. This is the core move of object-oriented code: keep a collection, act on it as a group, and let each object track itself.

Open exercises/28/01-critters.lua. It builds a list of critters and makes each play once. Add a second loop so each plays again, and watch the energies drop.

Filtering and counting objects

Because it's just a list, every Chapter 20 loop pattern works on objects too. Count how many critters are tired:

local tired = 0
for _, c in ipairs(critters) do
    if c:isTired() then
        tired = tired + 1
    end
end
print(tired .. " critters need a rest.")   -- 1 critters need a rest.

The if calls a method (c:isTired()) instead of testing a plain field, but the count pattern is unchanged.

Adding and removing objects

table.insert and table.remove (Chapter 24) work on a list of objects just like a list of numbers:

table.insert(critters, Critter.new("Zip", 5))   -- a new critter joins
table.remove(critters, 1)                        -- the first one leaves

Objects are values like any other — added, removed, sorted, and counted.

Homework

Homework files are in exercises/28/homework/.

Problem 1 — A party of heroes

Open exercises/28/homework/01-party.lua. A small Hero class is provided. Create a list of three heroes with different names and hit points, then loop over the list and print each one's name and HP on its own line.

Problem 2 — Total HP

Open exercises/28/homework/02-total-hp.lua. Use the accumulate pattern to add up everyone's HP and print the team total.

Problem 3 — Who is hurt?

Open exercises/28/homework/03-who-is-hurt.lua. Each hero has an hp and a max_hp. Loop over the party and print every hero whose hp is below max_hp.

Challenge — Strongest hero

Open exercises/28/homework/04-strongest.lua. Loop over the party and print the name of the hero with the most HP. Use the accumulate-a-maximum idea: remember the best so far, replacing it when you find a stronger one.

Stuck or finished? Open the homework solutions page.