Menu
Coddy logo textTech

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
end

Nota 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: Toyota

Questo schema mantiene il tuo codice DRY: la logica di inizializzazione condivisa risiede nel genitore, mentre ogni figlio gestisce solo i propri attributi unici.

challenge icon

Sfida

Facile

Costruiamo 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 __index e 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 usando Animal.new(Animal, name) per inizializzare il nome, poi aggiungere il campo wingspan all’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:

  1. Il nome dell’uccello
  2. 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 200

Il 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)
quiz iconMettiti alla prova

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

Esercitati da solo: Compilatore Lua online