Mixins vs Héritage
Fait partie de la section Object Oriented Programming du Journey Lua de Coddy — leçon 53 sur 70.
Vous avez 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 les fonctions directement dans une classe. Mais quand devriez-vous utiliser chaque approche ?
Utilisez l'héritage lorsque les objets partagent une identité fondamentale. Un Bird est un Animal — il est logique que Bird hérite des comportements animaux de base comme :eat() ou :sleep(). La relation est permanente et définit ce que l'objet est fondamentalement.
Utilisez les mixins lorsque vous souhaitez ajouter une fonctionnalité 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. Voler est une capacité, pas un trait caractéristique de l'oiseau. Un mixin Flyable vous permet d'ajouter la capacité de vol aux oiseaux qui en ont besoin, sans l'imposer à tous.
Les deux approches fonctionnent merveilleusement bien ensemble :
-- Bird hérite de Animal (relation de type "est un")
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})
-- 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 peuvent voler
applyMixin(Bird, Flyable)Ce modèle vous offre le meilleur des deux mondes : une hiérarchie de classes claire pour une identité partagée, ainsi que des capacités flexibles qui peuvent être intégrées là où c'est nécessaire.
Défi
FacileConstruisons un petit écosystème de créatures qui démontre quand utiliser l'héritage par rapport aux mixins ! Vous allez 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.
Vous organiserez votre code sur quatre fichiers :
Animal.lua: Créez la classe de base Animal avec un constructeur:new(name)qui stocke le nom. Incluez une méthode:eat()qui affiche{name} is eating. Cela représente l'identité fondamentale que tous les animaux partagent.Swimmable.lua: Créez un mixin avec une méthode:swim()qui affiche{self.name} is swimming. Il s'agit d'une capacité—toutes les créatures ne savent pas nager, elle ne doit donc pas faire partie de la chaîne d'héritage.Bird.lua: Créez une classe Bird qui hérite d'Animal. Le constructeur:new(name, canSwim)doit appeler le constructeur parent et stocker si cet oiseau peut nager. Appliquez le mixin Swimmable à la classe Bird. Incluez une méthode:chirp()qui affiche{name} chirps!—c'est un comportement spécifique aux oiseaux.main.lua: Écrivez la fonction utilitaireapplyMixinici. Lisez les entrées pour le nom d'un oiseau et s'il peut nager (yesouno). Créez une instance de Bird, puis démontrez ses capacités : appelez:eat()(hérité d'Animal), appelez:chirp()(méthode propre à Bird), et n'appelez:swim()que si l'oiseau peut nager.
Vous recevrez deux entrées :
- Le nom de l'oiseau (une chaîne de caractères)
- Si l'oiseau peut nager :
yesouno
Si l'oiseau peut nager, votre sortie devrait être :
{name} is eating
{name} chirps!
{name} is swimmingSi l'oiseau ne peut pas nager, votre sortie devrait être :
{name} is eating
{name} chirps!Par exemple, si les entrées sont Duck et yes, la sortie devrait être :
Duck is eating
Duck chirps!
Duck is swimmingSi les entrées sont Sparrow et no, la sortie devrait être :
Sparrow is eating
Sparrow chirps!Ce défi montre la distinction clé : Bird est un Animal (héritage), mais la nage est une capacité optionnelle (mixin). Le mixin est appliqué à la classe, mais votre logique principale décide de l'utiliser ou non en fonction des capacités individuelles de l'oiseau !
Aide-mémoire
Utilisez l'héritage lorsque les objets partagent une identité fondamentale (une relation « est un »). Utilisez les mixins pour ajouter des capacités qui ne définissent pas l'identité principale de l'objet.
L'héritage crée une relation permanente via la chaîne de prototypes :
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})Les mixins ajoutent des capacités optionnelles qui peuvent être appliquées de manière sélective :
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
applyMixin(Bird, Flyable)Les deux approches fonctionnent ensemble : utilisez l'héritage pour l'identité partagée et les mixins pour les capacités flexibles dont seules certaines instances ont besoin.
Essayez vous-même
-- main.lua: Point d'entrée - créer un oiseau et démontrer ses capacités
-- Fonction auxiliaire pour appliquer un mixin à une classe
local function applyMixin(class, mixin)
-- TODO: Copier toutes les méthodes du mixin vers la classe
end
-- Appliquer le mixin Swimmable à Bird avant de requérir Bird
-- Note: Vous devrez peut-être gérer cela dans Bird.lua ou ici
local Bird = require('Bird')
local Swimmable = require('Swimmable')
-- Appliquer le mixin à la classe Bird
applyMixin(Bird, Swimmable)
-- Lire les entrées
local name = io.read()
local canSwimInput = io.read()
-- TODO: Déterminer si l'oiseau peut nager en fonction de l'entrée ("yes" ou "no")
-- TODO: Créer une instance de Bird avec le nom et la capacité de nager
-- TODO: Appeler eat() - hérité d'Animal
-- TODO: Appeler chirp() - la propre méthode de Bird
-- TODO: Appeler swim() uniquement si l'oiseau peut nager
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Object Oriented Programming
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 Rectangle2Modè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 robots