Estendere il costruttore
Fa parte della sezione Programmazione orientata agli oggetti del percorso Lua di Coddy. Lezione 33 di 70.
Quando una classe figlia eredita da una classe genitore, spesso vuoi che il costruttore della figlia inizializzi tutto ciò che inizializzerebbe quello della genitore, oltre a eventuali campi aggiuntivi specifici della figlia. Invece di duplicare il codice, puoi chiamare direttamente il costruttore della classe genitore.
La tecnica consiste nell'usare il nome della classe genitore per invocare il suo metodo :new(), passando self come primo argomento. In questo modo si crea l'oggetto base con tutta l'inizializzazione del genitore, che poi personalizzi per la classe figlia.
local Vehicle = {}
Vehicle.__index = Vehicle
function Vehicle:new(speed)
local obj = {speed = speed}
setmetatable(obj, Vehicle)
return obj
end
-- Car eredita da Vehicle
local Car = {}
Car.__index = Car
setmetatable(Car, {__index = Vehicle})
function Car:new(speed, brand)
local obj = Vehicle.new(Vehicle, speed) -- Chiama il costruttore della classe genitrice
obj.brand = brand -- Aggiungi un campo specifico della classe figlia
setmetatable(obj, Car) -- Ricollega a Car
return obj
endNota i passaggi fondamentali nel costruttore della classe figlia: prima, chiamiamo Vehicle.new(Vehicle, speed) per ottenere un oggetto con il campo speed già impostato. Poi aggiungiamo il campo brand specifico della classe figlia. Infine, cambiamo la metatable da Vehicle a Car, in modo che l'istanza cerchi prima i metodi nella classe figlia.
Puoi anche usare la sintassi con i due punti quando chiami il costruttore della classe genitore: Vehicle:new(speed) è equivalente a Vehicle.new(Vehicle, speed), poiché entrambi passano Vehicle come primo argomento. Qui entrambe le forme funzionano correttamente, quindi sentiti libero di usare quella che trovi più leggibile.
local myCar = Car:new(120, "Toyota")
print(myCar.speed) -- Output: 120
print(myCar.brand) -- Output: ToyotaQuesto schema mantiene il tuo codice DRY: la logica di inizializzazione condivisa risiede nel genitore, mentre ogni figlio gestisce solo i propri attributi unici.
Sfida
FacileCostruiamo una gerarchia di creature in cui una classe figlia estende il costruttore della classe genitore! Creerai una classe genitore Animal e una classe figlia Bird, il cui costruttore chiama quello di Animal per gestire l’inizializzazione condivisa prima di aggiungere il proprio attributo specifico.
Organizzerai il codice in tre file:
Animal.lua: Definisci la classe genitore con un costruttore:new(name)che memorizza il nome dell’animale. Includi un metodo:describe()che stampa il nome dell’animale. Imposta il modello standard delle classi con__indexe restituisci la classe.Bird.lua: Definisci la classe figlia che eredita da Animal. Il costruttore:new(name, wingspan)di Bird deve chiamare il costruttore genitore usandoAnimal.new(Animal, name)per inizializzare il nome, poi aggiungere il campowingspanall’oggetto e infine ricollegare la metatable a Bird. Includi un metodo:info()che stampa sia il nome sia l’apertura alare in un formato specifico.main.lua: Richiedi il modulo Bird, leggi due input (nome e apertura alare), crea un’istanza di Bird e chiama entrambi i metodi per dimostrare che le funzionalità ereditate e quelle specifiche della classe figlia funzionano insieme.
Riceverai due input:
- Il nome dell’uccello
- L’apertura alare dell’uccello (un numero)
Nel tuo file principale, crea un Bird con il nome e l’apertura alare forniti. Poi chiama :describe() seguito da :info() su righe separate.
Il metodo :describe() (ereditato da Animal) deve stampare:
Animal: {name}Il metodo :info() (definito in Bird) deve stampare:
{name} has a wingspan of {wingspan}Per esempio, se gli input sono Eagle e 200, l’output dovrebbe essere:
Animal: Eagle
Eagle has a wingspan of 200Il punto chiave è che il costruttore di Bird non duplica l’inizializzazione del nome. Delega questo compito al costruttore di Animal, poi aggiunge solo ciò che è specifico degli uccelli. In questo modo il codice rimane DRY e si costruisce una corretta catena di ereditarietà!
Provalo tu
-- main.lua: Punto di ingresso
local Bird = require('Bird')
-- Leggi i dati di input
local name = io.read()
local wingspan = tonumber(io.read())
-- TODO: Crea un'istanza di Bird con il nome e l'apertura alare forniti
-- TODO: Chiama :describe() sull'uccello (ereditato da Animal)
-- TODO: Chiama :info() sull'uccello (definito in Bird)
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 formeEsercitati da solo: Compilatore Lua online