Mixins vs Inheritance
Part of the Object Oriented Programming section of Coddy's Lua journey — lesson 53 of 70.
You've learned two ways to share behavior: inheritance creates an "is-a" relationship through the prototype chain, while mixins copy functions directly into a class. But when should you use each approach?
Use inheritance when objects share a fundamental identity. A Bird is an Animal—it makes sense for Bird to inherit core animal behaviors like :eat() or :sleep(). The relationship is permanent and defines what the object fundamentally is.
Use mixins when you want to add a capability that doesn't define the object's core identity.
Not all birds can fly—penguins and ostriches are birds, but they don't fly. Flying is a capability, not a defining trait of being a bird. A Flyable mixin lets you add flight to birds that need it, without forcing it on all of them.
The two approaches work beautifully together:
-- Bird inherits from Animal (is-a relationship)
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})
-- Flyable is a capability (mixin)
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
-- Apply the mixin only to birds that can fly
applyMixin(Bird, Flyable)This pattern gives you the best of both worlds: a clear class hierarchy for shared identity, plus flexible capabilities that can be mixed in where needed.
Challenge
EasyLet's build a small ecosystem of creatures that demonstrates when to use inheritance versus mixins! You'll create an Animal base class, a Bird that inherits from it, and a Swimmable mixin—because while all birds are animals, not all birds can swim.
You'll organize your code across four files:
Animal.lua: Create the base Animal class with a constructor:new(name)that stores the name. Include an:eat()method that prints{name} is eating. This represents the core identity that all animals share.Swimmable.lua: Create a mixin with a:swim()method that prints{self.name} is swimming. This is a capability—not all creatures can swim, so it shouldn't be part of the inheritance chain.Bird.lua: Create a Bird class that inherits from Animal. The constructor:new(name, canSwim)should call the parent constructor and store whether this bird can swim. Apply the Swimmable mixin to the Bird class. Include a:chirp()method that prints{name} chirps!—this is a bird-specific behavior.main.lua: Write theapplyMixinhelper function here. Read inputs for a bird's name and whether it can swim (yesorno). Create a Bird instance, then demonstrate its abilities: call:eat()(inherited from Animal), call:chirp()(Bird's own method), and only call:swim()if the bird can swim.
You will receive two inputs:
- The bird's name (a string)
- Whether the bird can swim:
yesorno
If the bird can swim, your output should be:
{name} is eating
{name} chirps!
{name} is swimmingIf the bird cannot swim, your output should be:
{name} is eating
{name} chirps!For example, if the inputs are Duck and yes, the output should be:
Duck is eating
Duck chirps!
Duck is swimmingIf the inputs are Sparrow and no, the output should be:
Sparrow is eating
Sparrow chirps!This challenge shows the key distinction: Bird is an Animal (inheritance), but swimming is an optional capability (mixin). The mixin is applied to the class, but your main logic decides whether to use it based on the individual bird's abilities!
Cheat sheet
Use inheritance when objects share a fundamental identity (an "is-a" relationship). Use mixins when adding capabilities that don't define the object's core identity.
Inheritance creates a permanent relationship through the prototype chain:
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})Mixins add optional capabilities that can be applied selectively:
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
applyMixin(Bird, Flyable)The two approaches work together: use inheritance for shared identity and mixins for flexible capabilities that only some instances need.
Try it yourself
-- main.lua: Entry point - create a bird and demonstrate its abilities
-- Helper function to apply a mixin to a class
local function applyMixin(class, mixin)
-- TODO: Copy all methods from mixin to class
end
-- Apply the Swimmable mixin to Bird before requiring Bird
-- Note: You may need to handle this in Bird.lua or here
local Bird = require('Bird')
local Swimmable = require('Swimmable')
-- Apply mixin to Bird class
applyMixin(Bird, Swimmable)
-- Read inputs
local name = io.read()
local canSwimInput = io.read()
-- TODO: Determine if the bird can swim based on input ("yes" or "no")
-- TODO: Create a Bird instance with the name and swimming ability
-- TODO: Call eat() - inherited from Animal
-- TODO: Call chirp() - Bird's own method
-- TODO: Only call swim() if the bird can swim
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