Lua Academy · leksjon

Mixin-mønstre

Sett sammen funksjonalitet ved å kopiere metoder fra flere kildetabeller.

Leksjon 3 av 412 trinn

Mixin-mønstre er en gratis leksjon i Lua Academy på CoddyKit. Dette er leksjon 3 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Lua Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Lua Academy inneholder totalt 4 leksjoner.

Hva er en mixin?

En mixin er en tabell med metoder som kan kopieres inn i flere klasser. I motsetning til arv legger mixins til atferd uten å opprette et foreldre-barn-forhold. En klasse kan kombinere atferd fra flere mixins.

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()

Flere mixins

Bruk flere mixins på én klasse for å sette sammen kompleks atferd av enkle deler. Dette er horisontal komposisjon – hver mixin er uavhengig.

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"}))

Konflikter mellom mixins

Når to mixins definerer samme metodenavn, vinner den som ble brukt sist. Løs konflikter uttrykkelig ved å velge hvilken mixin-versjon som skal beholdes, eller pakk dem inn slik at begge kalles.

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()

Hjelpefunksjonen include()

En hjelpefunksjon gjør det enklere å bruke mixins: include(Class, Mixin) kopierer mixin-metoder inn i klassen og hopper over felt som allerede finnes, slik at eksisterende metoder ikke overskrives.

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)

Mixin med tilstand

Mixins som trenger tilstand per instans, initialiserer den i mixinens init-funksjon. Klassekonstruktøren kaller mixinens init-funksjoner for å sette opp hver mixins tilstand på instansen.

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")

Mixin-moduler

Plasser mixin-atferd i separate modulfiler. Hent dem inn i klassemodulen og bruk dem. Da holdes hver atferdsmessige bekymring i sin egen fil, og kodebasen blir mer oversiktlig.

-- 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)

Rollebaserte mixins

Tildel roller til objekter under kjøring ved å bruke mixin-funksjoner selektivt. Da kan atferden konfigureres per instans i stedet for per klasse.

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()

Kontrollere bruk av mixins

Hold oversikt over hvilke mixins som er brukt på en klasse, ved hjelp av et set. Dette hindrer at en mixin brukes flere ganger og gjør det mulig å inspisere klassens funksjonalitet.

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)  -- true

Mixins kontra arv kontra komposisjon

Tre komposisjonsstiler: arv (er-en, strengt hierarki), mixins (kopierer metoder, flatt) og komposisjon (har-en, delegering). Velg ut fra forholdet: et dyr ER-EN skapning (arv), en skapning KAN fly (mixin), en bil HAR en motor (komposisjon).

-- 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")

Mixin-mal

En standardmal for mixins: mixin-tabellen har metoder, en valgfri _init for tilstand per instans og et navnefelt for konfliktdeteksjon. Et konsekvent mønster gjør mixins komponerbare på tvers av prosjekter.

-- 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")

Mixin-spørsmål

Hvordan bruker du en mixin på en klasse?

Oppsummering: Mixin-mønstre

Mixins kopierer metoder fra en kildetabell til målklassen og setter sammen atferd uten arv. Bruk dem til gjennomgående funksjonalitet som serialisering, logging og hendelseshåndtering.

Gratis å komme i gang

Lær deg Lua med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
40
Leksjoner
159

Ofte stilte spørsmål

Er leksjonen «Mixin-mønstre» gratis?

Ja – du kan lese valgfritt 3 av leksjonene i læringsstien Lua Academy, inkludert «Mixin-mønstre», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i Lua Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «Mixin-mønstre»?

Sett sammen funksjonalitet ved å kopiere metoder fra flere kildetabeller. Du øver på Lua Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Lua Academy?

Ingen tidligere erfaring er nødvendig. Lua Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 3 av 4.

Hvor lang tid tar leksjonen «Mixin-mønstre»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Lua Academy-leksjonen?

Ja. Alle Lua Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Enkeltarv med __index-kjeding
  2. Kalle foreldremetoder med super
  3. Mixin-mønstre
  4. Overstyring og polymorfisme
← Tilbake til Lua Academy