Menu
Coddy logo textTech

Has-A Relationship

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

Throughout this course, you've used inheritance to share behavior between classes—a Dog "is an" Animal. But inheritance isn't always the right tool.

Sometimes, objects don't share a family tree; instead, one object simply contains another. This is called composition, and it models a "has-a" relationship.

Consider a car. A car isn't a type of engine—it has an engine. The engine is a separate object that exists inside the car:

local Engine = {}
Engine.__index = Engine

function Engine:new(horsepower)
    local obj = {horsepower = horsepower}
    setmetatable(obj, Engine)
    return obj
end

local Car = {}
Car.__index = Car

function Car:new(model, engine)
    local obj = {
        model = model,
        engine = engine  -- Store the Engine object inside Car
    }
    setmetatable(obj, Car)
    return obj
end

When creating a car, you pass an existing engine object to its constructor:

local myEngine = Engine:new(200)
local myCar = Car:new("Sedan", myEngine)

print(myCar.engine.horsepower)  -- 200

The car doesn't inherit from the engine—it simply holds a reference to one. This keeps both classes independent. You could swap in a different engine, or use the same engine class in a motorcycle.

Composition gives you flexibility that inheritance can't: you're assembling objects from parts rather than locking them into a rigid family hierarchy.

challenge icon

Challenge

Easy

Let's build a computer system using composition! Instead of inheritance, you'll create a Computer that has a CPU inside it—demonstrating the "has-a" relationship.

You'll organize your code across three files:

  • CPU.lua: Create a CPU class with a constructor :new(brand, speed) that stores the brand name and speed in GHz. Include a :getInfo() method that returns a string in the format: {brand} @ {speed}GHz
  • Computer.lua: Create a Computer class whose constructor :new(model, cpu) accepts a model name and a CPU object. The Computer stores the CPU object inside itself (composition!). Include a :describe() method that prints the computer's model and its CPU's info.
  • main.lua: Bring everything together! Read inputs for the CPU brand, CPU speed, and computer model. Create a CPU object first, then pass it to the Computer constructor. Finally, call the computer's :describe() method.

You will receive three inputs:

  1. The CPU brand (a string)
  2. The CPU speed in GHz (a number)
  3. The computer model name (a string)

Your output should show the computer's description, which includes information from its contained CPU:

Model: {model}
CPU: {brand} @ {speed}GHz

For example, if the inputs are Intel, 3.5, and WorkStation, the output should be:

Model: WorkStation
CPU: Intel @ 3.5GHz

Notice how the Computer doesn't inherit from CPU—it simply holds a reference to one. The Computer's :describe() method accesses the CPU's data through the stored object, showing how composition lets you build complex objects from simpler parts!

Cheat sheet

Objects can relate through composition, which models a "has-a" relationship. Instead of inheritance, one object contains another as a component.

Example of composition with a car that has an engine:

local Engine = {}
Engine.__index = Engine

function Engine:new(horsepower)
    local obj = {horsepower = horsepower}
    setmetatable(obj, Engine)
    return obj
end

local Car = {}
Car.__index = Car

function Car:new(model, engine)
    local obj = {
        model = model,
        engine = engine  -- Store the Engine object inside Car
    }
    setmetatable(obj, Car)
    return obj
end

Creating and using composed objects:

local myEngine = Engine:new(200)
local myCar = Car:new("Sedan", myEngine)

print(myCar.engine.horsepower)  -- 200

Composition provides flexibility by assembling objects from parts rather than using rigid inheritance hierarchies. Components remain independent and can be reused across different classes.

Try it yourself

-- main.lua
-- Bring everything together using composition

local CPU = require('CPU')
local Computer = require('Computer')

-- Read inputs
local cpuBrand = io.read()
local cpuSpeed = tonumber(io.read())
local computerModel = io.read()

-- TODO: Create a CPU object with the brand and speed

-- TODO: Create a Computer object with the model and the CPU object

-- TODO: Call the computer's describe() method to print the output
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