0Pricing
Lua Academy · Aula

Padrões de mixins

Componha comportamentos copiando métodos de várias tabelas de origem.

Padrões de mixins é uma aula grátis de Lua Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Lua Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Lua Academy inclui 4 aulas no total.

O que é um Mixin?

Um mixin é uma tabela de métodos que pode ser copiada para várias classes. Ao contrário da herança, os mixins adicionam comportamento sem estabelecer uma relação entre classe-pai e classe-filha. Uma classe pode combinar comportamentos de vários 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()

Vários Mixins

Aplique vários mixins a uma classe para compor um comportamento complexo a partir de partes simples. Esta é uma composição horizontal — cada mixin é independente.

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

Conflitos entre Mixins

Quando dois mixins definem o mesmo nome de método, vence o último aplicado. Resolva os conflitos explicitamente, escolhendo qual versão do mixin manter, ou envolva-os para chamar ambos.

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

Auxiliar include()

Uma função auxiliar torna a aplicação de mixins mais simples: include(Class, Mixin) copia os métodos do mixin para a classe, ignorando os campos que já existem (assim, os métodos existentes não são sobrescritos).

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 com Estado

Os mixins que precisam de estado por instância o inicializam na função de inicialização do mixin. O construtor da classe chama as inicializações dos mixins para configurar o estado de cada mixin na instância.

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

Módulos de Mixins

Empacote o comportamento dos mixins em arquivos de módulos separados. Exija-os no módulo da classe e aplique-os. Isso mantém cada responsabilidade comportamental em seu próprio arquivo e organiza a base de código.

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

Mixins Baseados em Papéis

Atribua papéis aos objetos em tempo de execução, aplicando funções de mixin seletivamente. Isso torna o comportamento configurável por instância, em vez de por 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()

Verificando a Aplicação de Mixins

Registre quais mixins foram aplicados a uma classe usando um conjunto. Isso evita aplicações duplicadas e permite inspecionar as capacidades de uma 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)  -- true

Mixins vs. Herança vs. Composição

Três estilos de composição: herança (é-um, hierarquia rígida), mixins (copiam métodos, estrutura plana) e composição (tem-um, delegação). Escolha com base na relação: animal IS-A criatura (herança), criatura CAN voar (mixin), carro HAS motor (composição).

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

Modelo de Mixin

Um modelo padrão de mixin: a tabela do mixin contém métodos, um _init opcional para o estado por instância e um campo de nome para detectar conflitos. Seguir um padrão consistente torna os mixins combináveis entre projetos.

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

Pergunta sobre Mixins

Como aplicar um mixin a uma classe?

Recapitulação: Padrões de Mixin

Os mixins copiam métodos de uma tabela de origem para a classe de destino, compondo comportamentos sem herança. Use-os para responsabilidades transversais, como serialização, registro de eventos e tratamento de eventos.

Perguntas Frequentes

A aula “Padrões de mixins” é grátis?

Sim — o texto completo de “Padrões de mixins” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Lua Academy, atualize para CoddyKit PRO. O curso de Lua Academy inclui 4 aulas no total.

O que vou aprender em “Padrões de mixins”?

Componha comportamentos copiando métodos de várias tabelas de origem. Você pratica Lua Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Lua Academy?

Nenhuma experiência prévia é necessária. Lua Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Padrões de mixins”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Lua Academy?

Sim. Cada aula de Lua Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Herança única com encadeamento de __index
  2. Chamando métodos da classe-pai com super
  3. Padrões de mixins
  4. Sobrescrita e polimorfismo
← Voltar para Lua Academy