Modèles de mixins
Composez des comportements en copiant des méthodes depuis plusieurs tables sources.
Modèles de mixins est une leçon Lua Academy gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Lua Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Lua Academy comprend 4 leçons au total.
Qu'est-ce qu'un module de composition ?
Un module de composition est un tableau de méthodes qui peuvent être copiées dans plusieurs classes. Contrairement à l'héritage, les modules de composition ajoutent un comportement sans établir de relation parent-enfant. Une classe peut combiner les comportements de plusieurs modules de composition.
local Flyable={}
function Flyable:fly()
print(self.name.." is flying!")
end
function Flyable:land()
print(self.name.." landed.")
end
local Bird={} Bird.__index=Bird
-- Mix in Flyable
for k,v in pairs(Flyable) do Bird[k]=v end
function Bird.new(name)
return setmetatable({name=name},Bird)
end
Bird.new("Eagle"):fly()Plusieurs modules de composition
Appliquez plusieurs modules de composition à une même classe pour composer un comportement complexe à partir d'éléments simples. Il s'agit d'une composition horizontale : chaque module de composition est indépendant.
local Serializable={}
function Serializable:toCSV(fields)
local row={}
for _,f in ipairs(fields) do row[#row+1]=tostring(self[f]) end
return table.concat(row,",")
end
local Loggable={}
function Loggable:log(msg)
print("["..os.date("%H:%M").."]["..self.name.."] "..msg)
end
local Entity={} Entity.__index=Entity
for k,v in pairs(Serializable) do Entity[k]=v end
for k,v in pairs(Loggable) do Entity[k]=v end
function Entity.new(n,v) return setmetatable({name=n,value=v},Entity) end
local e=Entity.new("item",42)
e:log("created"); print(e:toCSV({"name","value"}))Conflits entre modules de composition
Lorsque deux modules de composition définissent le même nom de méthode, le dernier appliqué l'emporte. Résolvez explicitement les conflits en choisissant la version du module à conserver, ou encapsulez-les pour appeler les deux.
local A={speak=function(self) print(self.name.." (A)") end}
local B={speak=function(self) print(self.name.." (B)") end}
local C={} C.__index=C
for k,v in pairs(A) do C[k]=v end
for k,v in pairs(B) do C[k]=v end -- B.speak overwrites A.speak
-- Explicit resolution:
local origA, origB = A.speak, B.speak
function C:speak() origA(self); origB(self) end -- call both
local c=setmetatable({name="X"},C)
c:speak()Fonction utilitaire include()
Une fonction utilitaire simplifie l'application des modules de composition : include(Class, Mixin) copie les méthodes du module dans la classe, en ignorant les champs qui existent déjà afin de ne pas écraser les méthodes existantes.
local function include(cls, mixin)
for k, v in pairs(mixin) do
if cls[k] == nil then
cls[k] = v
end
end
return cls
end
local Comparable={
equals=function(a,b) return a:compareTo(b)==0 end,
lt=function(a,b) return a:compareTo(b)<0 end,
}
local Temp={} Temp.__index=Temp
include(Temp, Comparable)
function Temp.new(c) return setmetatable({c=c},Temp) end
function Temp:compareTo(other) return self.c-other.c end
local a,b=Temp.new(100),Temp.new(50)
print(b.lt and b:lt(a)) -- true (b < a)Module de composition avec état
Les modules de composition qui nécessitent un état propre à chaque instance l'initialisent dans leur fonction d'initialisation. Le constructeur de la classe appelle les fonctions d'initialisation des modules pour configurer l'état de chacun d'eux dans l'instance.
local Observable={}
function Observable._init(self)
self._listeners={}
end
function Observable:on(event,fn)
self._listeners[event]=self._listeners[event] or {}
self._listeners[event][#self._listeners[event]+1]=fn
end
function Observable:emit(event,...)
for _,fn in ipairs(self._listeners[event] or {}) do fn(...) end
end
local Button={} Button.__index=Button
for k,v in pairs(Observable) do Button[k]=v end
function Button.new(label)
local self=setmetatable({label=label},Button)
Observable._init(self)
return self
end
local btn=Button.new("OK")
btn:on("click",function() print("Clicked!") end)
btn:emit("click")Modules de composition
Regroupez le comportement des modules de composition dans des fichiers de module distincts. Chargez-les dans le module de la classe, puis appliquez-les. Chaque aspect comportemental reste ainsi dans son propre fichier, ce qui organise mieux la base de code.
-- In a real project:
-- local Loggable = require("mixins.loggable")
-- local Cacheable = require("mixins.cacheable")
-- local MyClass = {}
-- for k,v in pairs(Loggable) do MyClass[k]=v end
-- for k,v in pairs(Cacheable) do MyClass[k]=v end
local TimestampMixin={}
function TimestampMixin:setCreated() self.createdAt=os.time() end
function TimestampMixin:age() return os.time()-self.createdAt end
local Record={} Record.__index=Record
for k,v in pairs(TimestampMixin) do Record[k]=v end
function Record.new(data)
local self=setmetatable(data or {},Record)
self:setCreated()
return self
end
print(Record.new({}).createdAt>0)Modules de composition selon les rôles
Attribuez des rôles aux objets lors de l'exécution en appliquant sélectivement des fonctions de module de composition. Le comportement devient ainsi configurable par instance plutôt que par classe.
local Roles={}
Roles.admin={deleteUser=function(self) print(self.name.." deletes user") end}
Roles.editor={editPost=function(self) print(self.name.." edits post") end}
Roles.viewer={viewPost=function(self) print(self.name.." views post") end}
local User={} User.__index=User
function User.new(name,...)
local self=setmetatable({name=name},User)
for _,role in ipairs({...}) do
for k,v in pairs(Roles[role] or {}) do self[k]=v end
end
return self
end
local alice=User.new("Alice","admin","editor")
alice:deleteUser(); alice:editPost()Vérifier l'application d'un module de composition
Suivez les modules de composition appliqués à une classe à l'aide d'un ensemble. Cela évite les applications en double et permet d'inspecter les capacités d'une classe.
local function applyMixin(cls, mixin, name)
cls._mixins = cls._mixins or {}
if cls._mixins[name] then return end -- already applied
for k,v in pairs(mixin) do
if k~="_init" then cls[k]=cls[k] or v end
end
cls._mixins[name] = true
end
local Fly={fly=function(self) print(self.name.." flies") end}
local Bird={} Bird.__index=Bird
applyMixin(Bird,Fly,"Fly")
applyMixin(Bird,Fly,"Fly") -- no-op, already applied
print(Bird._mixins.Fly) -- trueModules de composition, héritage et composition
Trois styles de composition : l'héritage (est-un, hiérarchie stricte), les modules de composition (copie de méthodes, structure plate) et la composition (possède-un, délégation). Choisissez selon la relation : un animal EST une créature (héritage), une créature PEUT voler (module de composition), une voiture POSSÈDE un moteur (composition).
-- Inheritance: "is-a"
-- local Dog = extends(Animal)
-- Mixin: "can-do"
-- include(Dog, Swimmable)
-- Composition: "has-a"
-- local Dog = {}
-- function Dog.new()
-- return {collar=Collar.new(),chip=Chip.new()}
-- end
print("Pick the right tool for the relationship")Modèle de module de composition
Un modèle standard de module de composition : le tableau du module contient des méthodes, une fonction _init facultative pour l'état propre à chaque instance et un champ de nom pour détecter les conflits. Suivre un modèle cohérent rend les modules de composition combinables entre les projets.
-- Standard mixin template:
local EventMixin = {}
EventMixin._name = "EventMixin"
function EventMixin._init(self)
self._events = {}
end
function EventMixin:on(e, fn)
self._events[e] = self._events[e] or {}
self._events[e][#self._events[e]+1] = fn
end
function EventMixin:trigger(e, ...)
for _, fn in ipairs(self._events[e] or {}) do fn(...) end
end
-- return EventMixin
print("EventMixin ready for use")Question sur les modules de composition
Comment appliquez-vous un module de composition à une classe ?
Récapitulatif : modèles de modules de composition
Les modules de composition copient des méthodes d'un tableau source dans la classe cible, composant ainsi un comportement sans héritage. Utilisez-les pour les préoccupations transversales comme la sérialisation, la journalisation et la gestion des événements.
Questions Fréquemment Posées
La leçon « Modèles de mixins » est-elle gratuite ?
Oui — le texte complet de « Modèles de mixins » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Lua Academy, passe à CoddyKit PRO. Le cours Lua Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Modèles de mixins » ?
Composez des comportements en copiant des méthodes depuis plusieurs tables sources. Tu pratiques Lua Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer Lua Academy ?
Aucune expérience préalable n'est requise. Lua Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.
Combien de temps prend la leçon « Modèles de mixins » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon Lua Academy ?
Oui. Chaque leçon Lua Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Héritage simple avec chaînage de __index
- Appeler les méthodes parentes avec super
- Modèles de mixins
- Redéfinition et polymorphisme