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.
Défi
FacileConstruisons 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’aideapplyMixin(target, mixin): elle copie chaque fonction du mixin dans la cible. Lis le nom de l’oiseau et indique s’il sait nager (yesouno). 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 :
- Le nom de l’oiseau (une chaîne de caractères)
- Si l’oiseau sait nager :
yesouno
Si l’oiseau sait nager, ta sortie doit être :
{name} is eating
{name} chirps!
{name} is swimmingSi 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 swimmingSi 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
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
1Le concept de « self »
Tables avec fonctions« self » expliciteLa syntaxe avec deux-pointsPoint vs deux-pointsRécapitulatif - Point mobile4Projet : Banque numérique
Configuration du projetMéthode de dépôt7Polymorphisme et Redéfinition
Redéfinition de méthodesAppeler des méthodes parentesDuck TypingInterface communeVérification du typeRécapitulatif - Rôles des employés10Projet : Gestionnaire de formes
Configuration du projetClasse RectangleClasse CercleMéthode PérimètreCollection de formesAire totaleFiltrer les formes2Modèle de prototype de classe
Le concept de prototypeLiaison avec __indexLe constructeur :new()Initialisation des attributsInstances indépendantesRécapitulatif - Usine de voitures5Surcharge d'opérateurs en POO
Additionner des objetsSoustraire des objetsConcaténer des objetsComparer des objets (<, >)Récapitulatif - Calculs de portefeuille8Encapsulation
Conventions de nommageClosures pour la confidentialitéAccès via les ClosuresTables en lecture seuleLogique de validationRécapitulatif - Coffre-fort sécurisé11Patrons de conception (Lite)
Fonctions FactoryTable SingletonPatron IteratorObserver (Écouteur)Récapitulatif - Logger Factory3État et comportement des objets
Variables d'instanceMéthodes GetterMéthodes SetterPropriétés calculéesFormatage de chaînesVérifications d'égalitéRécapitulatif - Note de l'étudiant6Bases de l'héritage
Mise en place de l'héritageHériter des méthodesÉtendre le constructeurAjouter des méthodes enfantsPartagé vs UniqueRécapitulatif - Hiérarchie des formes9Composition et Mixins
Relation 'A un'DélégationMixins simplesAppliquer plusieurs MixinsMixins vs HéritageRécapitulatif - Assemblage de robotsEntraînez-vous par vous-même : Compilateur Lua en ligne