Applying Multiple Mixins
Part of the Object Oriented Programming section of Coddy's Lua journey — lesson 52 of 70.
A single mixin adds one set of behaviors to a class. But real objects often need capabilities from multiple sources. A game object might need to move around and be visible on screen—two separate concerns that don't belong in the same mixin.
The solution is straightforward: apply multiple mixins to the same class. Each mixin contributes its own methods, and the class ends up with all of them:
local Movable = {}
function Movable:move(dx, dy)
self.x = self.x + dx
self.y = self.y + dy
end
local Visible = {}
function Visible:show()
print("Showing at " .. self.x .. ", " .. self.y)
end
function Visible:hide()
print("Hidden")
endApply both mixins to your class using the same helper function from before:
local function applyMixin(class, mixin)
for key, value in pairs(mixin) do
class[key] = value
end
end
local GameObject = {}
GameObject.__index = GameObject
applyMixin(GameObject, Movable)
applyMixin(GameObject, Visible)Now GameObject instances can both move and display themselves:
local obj = setmetatable({x = 0, y = 0}, GameObject)
obj:move(5, 3)
obj:show() -- Showing at 5, 3One caution: if two mixins define the same method name, the second one applied will overwrite the first. Keep your mixin methods distinct to avoid unexpected behavior.
Challenge
EasyLet's build a Character class for a game that combines multiple capabilities using mixins! Your character will be able to both attack enemies and heal itself—two distinct behaviors that come from separate mixins.
You'll organize your code across four files:
Attacker.lua: Create a mixin with an:attack(targetName)method that prints{self.name} attacks {targetName} for {self.damage} damage!(assumes the object hasnameanddamagefields)Healer.lua: Create a mixin with a:heal(amount)method that adds the amount toself.healthand prints{self.name} heals for {amount}. Health: {newHealth}Character.lua: Create a Character class with a constructor:new(name, health, damage)that stores all three attributes. Apply both the Attacker and Healer mixins so every character can attack and heal. Include a:status()method that prints{name} - Health: {health}, Damage: {damage}main.lua: Write theapplyMixinhelper function here. Read inputs for a character's name, health, damage, a target to attack, and a heal amount. Create a Character, show their status, have them attack the target, heal themselves, then show their status again.
You will receive five inputs:
- Character name (a string)
- Starting health (a number)
- Damage value (a number)
- Target name to attack (a string)
- Amount to heal (a number)
Your output should show the character using both mixin abilities:
{name} - Health: {health}, Damage: {damage}
{name} attacks {target} for {damage} damage!
{name} heals for {healAmount}. Health: {newHealth}
{name} - Health: {newHealth}, Damage: {damage}For example, if the inputs are Warrior, 100, 25, Goblin, and 30, the output should be:
Warrior - Health: 100, Damage: 25
Warrior attacks Goblin for 25 damage!
Warrior heals for 30. Health: 130
Warrior - Health: 130, Damage: 25Each mixin contributes its own method to the Character class. The character doesn't inherit from either mixin—it simply gains their abilities through the applyMixin function. This is the power of combining multiple mixins!
Cheat sheet
Multiple mixins can be applied to a single class to combine different sets of behaviors. Each mixin contributes its own methods to the class.
Define separate mixins for different concerns:
local Movable = {}
function Movable:move(dx, dy)
self.x = self.x + dx
self.y = self.y + dy
end
local Visible = {}
function Visible:show()
print("Showing at " .. self.x .. ", " .. self.y)
end
function Visible:hide()
print("Hidden")
endApply multiple mixins to a class using the applyMixin helper function:
local function applyMixin(class, mixin)
for key, value in pairs(mixin) do
class[key] = value
end
end
local GameObject = {}
GameObject.__index = GameObject
applyMixin(GameObject, Movable)
applyMixin(GameObject, Visible)The class now has methods from all applied mixins:
local obj = setmetatable({x = 0, y = 0}, GameObject)
obj:move(5, 3)
obj:show() -- Showing at 5, 3Important: If two mixins define the same method name, the second mixin applied will overwrite the first. Keep method names distinct across mixins to avoid conflicts.
Try it yourself
-- Main file
-- TODO: Write the applyMixin helper function
-- This function should copy all methods from a mixin table to a target table
local function applyMixin(target, mixin)
-- TODO: Implement mixin application
end
-- Make applyMixin available globally so Character.lua can use it
_G.applyMixin = applyMixin
local Character = require('Character')
-- Read inputs
local name = io.read()
local health = tonumber(io.read())
local damage = tonumber(io.read())
local targetName = io.read()
local healAmount = tonumber(io.read())
-- TODO: Create a Character with the given name, health, and damage
-- TODO: Show the character's status
-- TODO: Have the character attack the target
-- TODO: Have the character heal themselves
-- TODO: Show the character's status again
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