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
endWhen 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) -- 200The 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
EasyLet'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}GHzComputer.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:
- The CPU brand (a string)
- The CPU speed in GHz (a number)
- 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}GHzFor example, if the inputs are Intel, 3.5, and WorkStation, the output should be:
Model: WorkStation
CPU: Intel @ 3.5GHzNotice 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
endCreating and using composed objects:
local myEngine = Engine:new(200)
local myCar = Car:new("Sedan", myEngine)
print(myCar.engine.horsepower) -- 200Composition 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
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1The 'Self' Concept
Tables with FunctionsExplicit 'self'The Colon SyntaxDot vs ColonRecap - Moving Point4Project: Digital Bank
Project SetupDeposit Method7Polymorphism & Overriding
Overriding MethodsCalling Parent MethodsDuck TypingCommon InterfaceChecking TypeRecap - Employee Roles10Project: Shape Manager
Project SetupRectangle Class2Class Prototype Pattern
The Prototype ConceptLinking with __indexThe :new() ConstructorInitializing AttributesIndependent InstancesRecap - Car Factory5Operator Overloading in OOP
Adding ObjectsSubtracting ObjectsConcatenating ObjectsComparing Objects (<, >)Recap - Wallet Math8Encapsulation
Naming ConventionsClosures for PrivacyAccess via ClosuresRead-Only TablesValidation LogicRecap - Secure Vault11Design Patterns (Lite)
Factory FunctionsSingleton TableIterator PatternObserver (Listener)Recap - Logger Factory3Object State and Behavior
Instance VariablesGetter MethodsSetter MethodsCalculated PropertiesFormatting StringsEquality ChecksRecap - Student Grade6Inheritance Basics
The Inheritance SetupInheriting MethodsExtending the ConstructorAdding Child MethodsShared vs UniqueRecap - Shape Hierarchy9Composition & Mixins
Has-A RelationshipDelegationSimple MixinsApplying Multiple MixinsMixins vs InheritanceRecap - Robot Assembly