Mixin vs ereditarietà
Fa parte della sezione Programmazione orientata agli oggetti del percorso Lua di Coddy. Lezione 53 di 70.
Hai imparato due modi per condividere il comportamento: l'ereditarietà crea una relazione «è un» attraverso la catena dei prototipi, mentre i mixin copiano le funzioni direttamente in una classe. Ma quando dovresti usare ciascun approccio?
Usa l'ereditarietà quando gli oggetti condividono un'identità fondamentale. Un Bird è un Animal. Ha senso che Bird erediti comportamenti fondamentali degli animali come :eat() o :sleep(). La relazione è permanente e definisce ciò che l'oggetto è fondamentalmente.
Usa i mixins quando vuoi aggiungere una capacità che non definisce l’identità fondamentale dell’oggetto.
Non tutti gli uccelli possono volare: i pinguini e gli struzzi sono uccelli, ma non volano. Volare è una capacità, non una caratteristica distintiva dell'essere un uccello. Un mixin Flyable ti permette di aggiungere la capacità di volare agli uccelli che ne hanno bisogno, senza imporla a tutti.
I due approcci funzionano benissimo insieme:
-- Bird eredita da Animal (relazione «è un»)
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})
-- Flyable è una capacità (mixin)
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
-- Applica il mixin solo agli uccelli che possono volare
applyMixin(Bird, Flyable)Questo schema ti offre il meglio di entrambi i mondi: una gerarchia di classi chiara per un’identità condivisa, più capacità flessibili che possono essere combinate quando necessario.
Sfida
FacileCostruiamo un piccolo ecosistema di creature che mostri quando usare l'ereditarietà invece dei mixin! Creerai una classe base Animal, una classe Bird che eredita da essa e un mixin Swimmable, perché, sebbene tutti gli uccelli siano animali, non tutti gli uccelli sanno nuotare.
Organizzerai il codice in quattro file:
Animal.lua: crea la classe base Animal con un costruttore:new(name)che memorizza il nome. Includi un metodo:eat()che stampa{name} is eating. Questo rappresenta l'identità fondamentale condivisa da tutti gli animali.Swimmable.lua: crea un mixin con un metodo:swim()che stampa{self.name} is swimming. Questa è una capacità: non tutte le creature sanno nuotare, quindi non dovrebbe far parte della catena di ereditarietà.Bird.lua: crea una classe Bird che eredita da Animal. Il costruttore:new(name, canSwim)dovrebbe chiamare il costruttore della classe genitore e memorizzare se questo uccello sa nuotare. Applica il mixin Swimmable alla classe Bird. Includi un metodo:chirp()che stampa{name} chirps!. Questo è un comportamento specifico degli uccelli.main.lua: scrivi qui la funzione di supportoapplyMixin. Leggi gli input relativi al nome dell'uccello e alla possibilità che sappia nuotare (yesono). Crea un'istanza di Bird, quindi mostra le sue capacità: chiama:eat()(ereditato da Animal), chiama:chirp()(metodo proprio di Bird) e chiama:swim()solo se l'uccello sa nuotare.
Riceverai due input:
- Il nome dell'uccello (una stringa)
- Se l'uccello sa nuotare:
yesono
Se l'uccello sa nuotare, l'output dovrebbe essere:
{name} is eating
{name} chirps!
{name} is swimmingSe l'uccello non sa nuotare, l'output dovrebbe essere:
{name} is eating
{name} chirps!Per esempio, se gli input sono Duck e yes, l'output dovrebbe essere:
Duck is eating
Duck chirps!
Duck is swimmingSe gli input sono Sparrow e no, l'output dovrebbe essere:
Sparrow is eating
Sparrow chirps!Questa sfida mostra la distinzione fondamentale: Bird è un Animal (ereditarietà), mentre nuotare è una capacità opzionale (mixin). Il mixin viene applicato alla classe, ma la logica principale decide se usarlo in base alle capacità del singolo uccello!
Provalo tu
-- main.lua: Punto de entrada: crea un pájaro y demuestra sus habilidades
-- Función auxiliar para aplicar un mixin a una clase
local function applyMixin(class, mixin)
-- TODO: Copia todos los métodos de mixin a class
end
-- Aplica el mixin Swimmable a Bird antes de cargar Bird
-- Nota: Puede que necesites gestionar esto en Bird.lua o aquí
local Bird = require('Bird')
local Swimmable = require('Swimmable')
-- Aplica el mixin a la clase Bird
applyMixin(Bird, Swimmable)
-- Lee las entradas
local name = io.read()
local canSwimInput = io.read()
-- TODO: Determina si el pájaro puede nadar según la entrada ("yes" o "no")
-- TODO: Crea una instancia de Bird con el nombre y la capacidad de nadar
-- TODO: Llama a eat(), heredado de Animal
-- TODO: Llama a chirp(), el método propio de Bird
-- TODO: Llama a swim() solo si el pájaro puede nadar
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Il concetto di «self»
Tabelle con funzioni«self» esplicitoLa sintassi dei due puntiPunto o due puntiRiepilogo - Spostare il punto4Progetto: Banca digitale
Configurazione del progettoMetodo di deposito2Schema del prototipo di classe
Il concetto di prototipoCollegare con __indexIl costruttore :new()Inizializzare gli attributiIstanze indipendentiRiepilogo - Fabbrica di auto5Sovraccarico degli operatori nella OOP
Sommare oggettiSottrarre oggettiConcatenare oggettiConfrontare oggetti (<, >)Ripasso - Calcoli con il portafoglio8Incapsulamento
Convenzioni di denominazioneClosure per la privacyAccesso tramite closureTabelle di sola letturaLogica di convalidaRiepilogo - Cassaforte sicura11Modelli di progettazione (base)
Funzioni factoryTabella singletonPattern IteratorObserver (listener)Riepilogo - Factory per logger3Stato e comportamento degli oggetti
Variabili di istanzaMetodi getterMetodi setterProprietà calcolateFormattazione delle stringheControlli di uguaglianzaRiepilogo - Voto dello studente6Fondamenti dell'ereditarietà
Configurare l'ereditarietàEreditare i metodiEstendere il costruttoreAggiungere metodi alla classe figliaCondiviso vs specificoRiepilogo - Gerarchia delle forme9Composizione e mixin
Relazione Has-ADelegaMixin sempliciApplicare più mixinMixin vs ereditarietàRiepilogo - Assemblaggio di robotEsercitati da solo: Compilatore Lua online