Menu
CoddyTech

Mixins vs Héritage

Fait partie de la section Programmation orientée objet du Journey Lua de Coddy. Leçon 53 sur 70.

Tu as appris deux façons de partager un comportement : l’héritage crée une relation « est un » via la chaîne de prototypes, tandis que les mixins copient directement des fonctions dans une classe. Mais quand faut-il utiliser chaque approche ?

Utilise l’héritage lorsque des objets partagent une identité fondamentale. Un Bird est un Animal. Il est logique que Bird hérite des comportements fondamentaux des animaux, comme :eat() ou :sleep(). Cette relation est permanente et définit ce qu’est fondamentalement l’objet.

Utilisez des mixins lorsque vous souhaitez ajouter une capacité qui ne définit pas l’identité fondamentale de l’objet.

Tous les oiseaux ne peuvent pas voler : les manchots et les autruches sont des oiseaux, mais ils ne volent pas. Le vol est une capacité, et non un trait définissant ce qu’est un oiseau. Un mixin Flyable permet d’ajouter la capacité de voler aux oiseaux qui en ont besoin, sans l’imposer à tous.

Les deux approches fonctionnent ensemble. Ici, Eagle et Penguin héritent tous deux de Bird, et seul Eagle reçoit le mixin :

-- Bird hérite de Animal (relation « est un »)
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})

-- Eagle et Penguin sont tous deux des oiseaux
local Eagle = setmetatable({}, {__index = Bird})
Eagle.__index = Eagle
local Penguin = setmetatable({}, {__index = Bird})
Penguin.__index = Penguin

-- Flyable est une capacité (mixin)
local Flyable = {}
function Flyable:fly()
    print(self.name .. " soars through the sky!")
end

-- Appliquer le mixin uniquement aux oiseaux qui volent
applyMixin(Eagle, Flyable)

Un aigle peut appeler :fly(). Un pingouin ne le peut pas : fly ne figure ni dans Penguin, ni dans Bird, ni dans Animal, donc penguin:fly() provoque une erreur. Appliquer le mixin à Bird à la place copierait fly dans chaque oiseau, y compris les pingouins.

challenge icon

Défi

Facile

Construisons un petit écosystème de créatures qui montre quand utiliser l’héritage plutôt que les mixins ! Tu vas créer une classe de base Animal, un Bird qui en hérite et un mixin Swimmable, car si tous les oiseaux sont des animaux, tous les oiseaux ne savent pas nager.

Tu vas organiser ton code dans quatre fichiers :

  • Animal.lua : crée la classe de base Animal avec un constructeur :new(name) qui stocke le nom. Ajoute une méthode :eat() qui affiche {name} is eating. Cela représente l’identité fondamentale commune à tous les animaux.
  • Swimmable.lua : crée un mixin avec une méthode :swim() qui affiche {self.name} is swimming. Il s’agit d’une capacité que toutes les créatures ne possèdent pas ; elle ne devrait donc pas faire partie de la chaîne d’héritage.
  • Bird.lua : crée une classe Bird qui hérite d’Animal. Le constructeur :new(name) doit appeler le constructeur parent. Ajoute une méthode :chirp() qui affiche {name} chirps!. C’est un comportement propre aux oiseaux. N’ajoute pas la nage à Bird : tous les oiseaux ne savent pas nager.
  • main.lua : écris ici la fonction d’aide applyMixin(target, mixin) : elle copie chaque fonction du mixin dans la cible. Lis le nom de l’oiseau et indique s’il sait nager (yes ou no). Crée un Bird et, s’il sait nager, applique le mixin Swimmable à cet oiseau seulement. Appelle ensuite :eat() (héritée d’Animal) et :chirp() (la méthode propre à Bird), puis appelle :swim() uniquement si cet oiseau possède cette méthode (if bird.swim then).

Tu recevras deux entrées :

  1. Le nom de l’oiseau (une chaîne de caractères)
  2. Si l’oiseau sait nager : yes ou no

Si l’oiseau sait nager, ta sortie doit être :

{name} is eating
{name} chirps!
{name} is swimming

Si l’oiseau ne sait pas nager, ta sortie doit être :

{name} is eating
{name} chirps!

Par exemple, si les entrées sont Duck et yes, la sortie doit être :

Duck is eating
Duck chirps!
Duck is swimming

Si les entrées sont Sparrow et no, la sortie doit être :

Sparrow is eating
Sparrow chirps!

Ce défi montre la distinction essentielle : Bird est un Animal (héritage), tandis que la nage est une capacité facultative (mixin). Comme le mixin est appliqué uniquement aux oiseaux qui savent nager, un oiseau qui ne sait pas nager n’a pas du tout de méthode swim.

Essayez vous-même

-- main.lua: Point d’entrée - créer un oiseau et montrer ses capacités

-- Fonction utilitaire pour appliquer un mixin à une classe ou à un seul objet
local function applyMixin(target, mixin)
    -- TODO: Copier toutes les fonctions de mixin dans target

end

local Bird = require('Bird')
local Swimmable = require('Swimmable')

-- Lire les entrées
local name = io.read()
local canSwimInput = io.read()

-- TODO: Créer une instance de Bird avec le nom

-- TODO: Si l’entrée est "yes", appliquer le mixin Swimmable uniquement à cet oiseau

-- TODO: Appeler eat() - héritée de Animal

-- TODO: Appeler chirp() - méthode propre à Bird

-- TODO: Appeler swim() uniquement si cet oiseau possède cette méthode
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Compilateur Lua en ligne