Menu
Coddy logo textTech

Delegation

Part of the Object Oriented Programming section of Coddy's Lua journey — lesson 50 of 70.

Storing an object inside another object is only the first step. The real power of composition comes from delegation—when the outer object forwards method calls to its inner components.

Think about starting a car. You don't reach under the hood and manually spin the engine. Instead, you turn the key, and the car handles the rest internally.

In code, this means the Car class provides a simple :start() method that delegates the actual work to the engine:

function Car:start()
    print("Turning the key...")
    self.engine:run()  -- Delegate to the inner object
end

The car doesn't need to know how the engine runs—it just tells the engine to do its job. This keeps responsibilities separated. The Engine class handles engine logic, while Car coordinates the pieces:

function Engine:run()
    print("Engine running at " .. self.horsepower .. " HP")
end

local engine = Engine:new(250)
local car = Car:new("Coupe", engine)
car:start()
-- Output:
-- Turning the key...
-- Engine running at 250 HP

Delegation creates a clean interface for users of your class. They interact with the car, not its internal parts. If you later swap in a different engine implementation, the car's public interface stays the same—only the internal delegation changes.

challenge icon

Challenge

Easy

Let's build a music player system that demonstrates delegation! You'll create a Speaker component and a MusicPlayer that contains it. The player won't handle sound output directly—instead, it will delegate that responsibility to its internal speaker.

You'll organize your code across three files:

  • Speaker.lua: Create a Speaker class with a constructor :new(volume) that stores the volume level. Include a :playSound(trackName) method that prints: Playing "{trackName}" at volume {volume}
  • MusicPlayer.lua: Create a MusicPlayer class whose constructor :new(brand, speaker) accepts a brand name and a Speaker object. The player stores the speaker inside itself. Include these methods:
    • :play(trackName) — prints {brand} player starting... then delegates to the speaker's :playSound() method
    • :stop() — prints {brand} player stopped
  • main.lua: Bring everything together! Read inputs for the speaker volume, player brand, and a track name. Create a Speaker first, then pass it to the MusicPlayer constructor. Call the player's :play() method with the track name, then call :stop().

You will receive three inputs:

  1. The speaker volume (a number)
  2. The music player brand (a string)
  3. The track name to play (a string)

Your output should show the player coordinating with its speaker:

{brand} player starting...
Playing "{trackName}" at volume {volume}
{brand} player stopped

For example, if the inputs are 75, SoundMax, and Summer Vibes, the output should be:

SoundMax player starting...
Playing "Summer Vibes" at volume 75
SoundMax player stopped

Notice how the MusicPlayer doesn't know how to produce sound—it simply tells its speaker to handle that part. This is delegation in action: the outer object provides a clean interface while the inner component does the specialized work!

Cheat sheet

Delegation allows an outer object to forward method calls to its inner components, keeping responsibilities separated and creating clean interfaces.

The outer object provides simple methods that delegate work to inner objects:

function Car:start()
    print("Turning the key...")
    self.engine:run()  -- Delegate to the inner object
end

The inner object handles its own specialized logic:

function Engine:run()
    print("Engine running at " .. self.horsepower .. " HP")
end

local engine = Engine:new(250)
local car = Car:new("Coupe", engine)
car:start()
-- Output:
-- Turning the key...
-- Engine running at 250 HP

Benefits of delegation:

  • The outer class doesn't need to know how the inner object works
  • Users interact with a clean public interface
  • Internal implementations can be swapped without changing the public interface

Try it yourself

-- main.lua
-- Bring everything together!

local Speaker = require('Speaker')
local MusicPlayer = require('MusicPlayer')

-- Read inputs
local volume = tonumber(io.read())
local brand = io.read()
local trackName = io.read()

-- TODO: Create a Speaker with the given volume

-- TODO: Create a MusicPlayer with the brand and speaker

-- TODO: Call the player's play method with the track name

-- TODO: Call the player's stop method
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming