Mixins vs Herança
Parte da seção Object Oriented Programming do Journey de Lua da Coddy — lição 53 de 70.
Você aprendeu duas maneiras de compartilhar comportamento: a herança cria uma relação "é-um" através da cadeia de protótipos, enquanto os mixins copiam funções diretamente para uma classe. Mas quando você deve usar cada abordagem?
Use herança quando os objetos compartilham uma identidade fundamental. Um Bird é um Animal — faz sentido que Bird herde comportamentos essenciais de animais, como :eat() ou :sleep(). A relação é permanente e define o que o objeto fundamentalmente é.
Use mixins quando você quiser adicionar uma funcionalidade que não define a identidade central do objeto.
Nem todas as aves podem voar — pinguins e avestruzes são aves, mas não voam. Voar é uma capacidade, não um traço definidor de ser uma ave. Um mixin Flyable permite adicionar voo às aves que precisam dele, sem forçá-lo em todas elas.
As duas abordagens funcionam perfeitamente juntas:
-- Bird herda de Animal (relacionamento é-um)
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})
-- Flyable é uma capacidade (mixin)
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
-- Aplica o mixin apenas a pássaros que podem voar
applyMixin(Bird, Flyable)Este padrão oferece o melhor dos dois mundos: uma hierarquia de classes clara para identidade compartilhada, além de capacidades flexíveis que podem ser misturadas onde for necessário.
Desafio
FácilVamos construir um pequeno ecossistema de criaturas que demonstra quando usar herança versus mixins! Você criará uma classe base Animal, um Bird que herda dela e um mixin Swimmable—porque, embora todos os pássaros sejam animais, nem todos os pássaros sabem nadar.
Você organizará seu código em quatro arquivos:
Animal.lua: Crie a classe base Animal com um construtor:new(name)que armazena o nome. Inclua um método:eat()que imprime{name} is eating. Isso representa a identidade central que todos os animais compartilham.Swimmable.lua: Crie um mixin com um método:swim()que imprime{self.name} is swimming. Esta é uma capacidade—nem todas as criaturas sabem nadar, por isso não deve fazer parte da cadeia de herança.Bird.lua: Crie uma classe Bird que herda de Animal. O construtor:new(name, canSwim)deve chamar o construtor pai e armazenar se este pássaro pode nadar. Aplique o mixin Swimmable à classe Bird. Inclua um método:chirp()que imprime{name} chirps!—este é um comportamento específico de pássaros.main.lua: Escreva a função auxiliarapplyMixinaqui. Leia as entradas para o nome de um pássaro e se ele pode nadar (yesouno). Crie uma instância de Bird e, em seguida, demonstre suas habilidades: chame:eat()(herdado de Animal), chame:chirp()(método próprio de Bird) e chame:swim()apenas se o pássaro puder nadar.
Você receberá duas entradas:
- O nome do pássaro (uma string)
- Se o pássaro pode nadar:
yesouno
Se o pássaro puder nadar, sua saída deve ser:
{name} is eating
{name} chirps!
{name} is swimmingSe o pássaro não puder nadar, sua saída deve ser:
{name} is eating
{name} chirps!Por exemplo, se as entradas forem Duck e yes, a saída deve ser:
Duck is eating
Duck chirps!
Duck is swimmingSe as entradas forem Sparrow e no, a saída deve ser:
Sparrow is eating
Sparrow chirps!Este desafio mostra a distinção fundamental: Bird é um Animal (herança), mas nadar é uma capacidade opcional (mixin). O mixin é aplicado à classe, mas sua lógica principal decide se deve usá-lo com base nas habilidades individuais do pássaro!
Folha de consulta
Use herança quando objetos compartilham uma identidade fundamental (uma relação "é-um"). Use mixins ao adicionar capacidades que não definem a identidade central do objeto.
A herança cria um relacionamento permanente por meio da cadeia de protótipos:
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})Mixins adicionam capacidades opcionais que podem ser aplicadas seletivamente:
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
applyMixin(Bird, Flyable)As duas abordagens funcionam juntas: use herança para identidade compartilhada e mixins para capacidades flexíveis que apenas algumas instâncias precisam.
Experimente você mesmo
-- main.lua: Ponto de entrada - cria um pássaro e demonstra suas habilidades
-- Função auxiliar para aplicar um mixin a uma classe
local function applyMixin(class, mixin)
-- TODO: Copiar todos os métodos do mixin para a classe
end
-- Aplica o mixin Swimmable ao Bird antes de requerer Bird
-- Nota: Você pode precisar lidar com isso em Bird.lua ou aqui
local Bird = require('Bird')
local Swimmable = require('Swimmable')
-- Aplica o mixin à classe Bird
applyMixin(Bird, Swimmable)
-- Lê as entradas
local name = io.read()
local canSwimInput = io.read()
-- TODO: Determinar se o pássaro pode nadar com base na entrada ("yes" ou "no")
-- TODO: Criar uma instância de Bird com o nome e a habilidade de nadar
-- TODO: Chamar eat() - herdado de Animal
-- TODO: Chamar chirp() - método próprio do Bird
-- TODO: Chamar swim() apenas se o pássaro puder nadar
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Object Oriented Programming
1O Conceito 'Self'
Tabelas com Funções'self' ExplícitoA Sintaxe de Dois-pontosPonto vs Dois-pontosRecapitulação - Movendo o Ponto4Projeto: Banco Digital
Configuração do ProjetoMétodo de Depósito7Polimorfismo e Sobrescrita
Sobrescrita de MétodosChamando Métodos da Classe PaiDuck TypingInterface ComumVerificando o TipoRecapitulação - Papéis de Funcionários10Projeto: Gerenciador de Formas
Configuração do ProjetoClasse Retângulo2Padrão de Protótipo de Classe
O Conceito de ProtótipoVinculando com __indexO Construtor :new()Inicializando AtributosInstâncias IndependentesRecapitulação - Fábrica de Carros5Sobrecarga de Operadores em POO
Somando ObjetosSubtraindo ObjetosConcatenando ObjetosComparando Objetos (<, >)Recapitulação - Matemática de Carteira8Encapsulamento
Convenções de NomenclaturaClosures para PrivacidadeAcesso via ClosuresTabelas Somente LeituraLógica de ValidaçãoRecapitulação - Cofre Seguro11Padrões de Projeto (Lite)
Funções FactoryTabela SingletonPadrão IteratorObserver (Listener)Recapitulação - Logger Factory3Estado e Comportamento do Objeto
Variáveis de InstânciaMétodos GetterMétodos SetterPropriedades CalculadasFormatação de StringsVerificações de IgualdadeRecapitulação - Nota do Aluno6Fundamentos de Herança
Configurando a HerançaHerdando MétodosEstendendo o ConstrutorAdicionando Métodos FilhosCompartilhado vs ÚnicoRecapitulação - Hierarquia de Formas9Composição e Mixins
Relacionamento Tem-UmDelegaçãoMixins SimplesAplicando Múltiplos MixinsMixins vs HerançaRecapitulação - Montagem de Robô