Mixins vs Herança
Parte da seção Programação orientada a objetos do Journey de Lua da Coddy. Lição 53 de 70.
Você aprendeu duas maneiras de compartilhar comportamentos: a herança cria uma relação de “é um” por meio 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 básicos de animais, como :eat() ou :sleep(). A relação é permanente e define o que o objeto é fundamentalmente.
Use mixins quando quiser adicionar uma capacidade que não define a identidade central do objeto.
Nem todas as aves conseguem voar: pinguins e avestruzes são aves, mas não voam. Voar é uma capacidade, não uma característica definidora de ser uma ave. Um mixin Flyable permite adicionar a capacidade de voar às aves que precisam dela, sem obrigar todas a voar.
As duas abordagens funcionam juntas. Aqui, Eagle e Penguin herdam de Bird, e apenas Eagle recebe o mixin:
-- Bird herda de Animal (relação "é um")
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})
-- Eagle e Penguin são ambos pássaros
local Eagle = setmetatable({}, {__index = Bird})
Eagle.__index = Eagle
local Penguin = setmetatable({}, {__index = Bird})
Penguin.__index = Penguin
-- Flyable é uma capacidade (mixin)
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
-- Aplique o mixin apenas aos pássaros que voam
applyMixin(Eagle, Flyable)Uma águia pode chamar :fly(). Um pinguim não pode: fly não está em Penguin, Bird ou Animal, então penguin:fly() é um erro. Aplicar o mixin a Bird em vez disso copiaria fly para todas as aves, incluindo os pinguins.
Desafio
FácilVamos criar um pequeno ecossistema de criaturas que demonstra quando usar herança em vez de mixins! Você vai criar uma classe base Animal, uma Bird que herda dela e um mixin Swimmable, pois, embora todos os pássaros sejam animais, nem todos conseguem nadar.
Você vai organizar seu código em quatro arquivos:
Animal.lua: Crie a classe base Animal com um construtor:new(name)que armazene o nome. Inclua um método:eat()que imprima{name} is eating. Isso representa a identidade fundamental compartilhada por todos os animais.Swimmable.lua: Crie um mixin com um método:swim()que imprima{self.name} is swimming. Essa é uma capacidade: nem todas as criaturas conseguem nadar, então ela não deve fazer parte da cadeia de herança.Bird.lua: Crie uma classe Bird que herde de Animal. O construtor:new(name)deve chamar o construtor da classe pai. Inclua um método:chirp()que imprima{name} chirps!. Esse é um comportamento específico dos pássaros. Não inclua a capacidade de nadar em Bird: nem todo pássaro consegue nadar.main.lua: Escreva aqui a função auxiliarapplyMixin(target, mixin): ela copia todas as funções do mixin para o alvo. Leia o nome do pássaro e se ele consegue nadar (yesouno). Crie um Bird e, se ele conseguir nadar, aplique o mixin Swimmable somente a esse pássaro. Em seguida, chame:eat()(herdado de Animal) e:chirp()(método próprio de Bird), e chame:swim()somente se esse pássaro tiver esse método (if bird.swim then).
Você receberá duas entradas:
- O nome do pássaro (uma string)
- Se o pássaro consegue nadar:
yesouno
Se o pássaro conseguir nadar, sua saída deverá ser:
{name} is eating
{name} chirps!
{name} is swimmingSe o pássaro não conseguir nadar, sua saída deverá ser:
{name} is eating
{name} chirps!Por exemplo, se as entradas forem Duck e yes, a saída deverá ser:
Duck is eating
Duck chirps!
Duck is swimmingSe as entradas forem Sparrow e no, a saída deverá ser:
Sparrow is eating
Sparrow chirps!Este desafio mostra a distinção fundamental: Bird é um Animal (herança), mas nadar é uma capacidade opcional (mixin). Como o mixin é aplicado somente aos pássaros que conseguem nadar, um pássaro que não consegue nadar não tem nenhum método swim.
Experimente você mesmo
-- main.lua: Ponto de entrada - crie um pássaro e demonstre suas habilidades
-- Função auxiliar para aplicar um mixin a uma classe ou a um único objeto
local function applyMixin(target, mixin)
-- TODO: Copie todas as funções de mixin para target
end
local Bird = require('Bird')
local Swimmable = require('Swimmable')
-- Leia as entradas
local name = io.read()
local canSwimInput = io.read()
-- TODO: Crie uma instância de Bird com o nome
-- TODO: Se a entrada for "yes", aplique o mixin Swimmable apenas a este pássaro
-- TODO: Chame eat() - herdado de Animal
-- TODO: Chame chirp() - método próprio de Bird
-- TODO: Chame swim() apenas se este pássaro tiver esse método
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação orientada a objetos
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ânguloClasse CírculoMétodo PerímetroColeção de FormasÁrea TotalFiltrar Formas2Padrã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ôPratique por conta própria: Compilador de Lua online