In questo e nel successivo capitolo vedremo in maggiore dettaglio gli aspetti più interessanti, e utili, della programmazione OO in Ruby. Completeremo il discorso sulle classi iniziato nel capitolo 8 introducendo in questo capitolo l'ereditarietà singola e nel prossimo l'ereditarietà multipla.
Ricominciamo da dove eravamo rimasti. Alla fine del capitolo 8 avevamo due classi CarroArmato
e Camion
:
class CarroArmato attr_reader :colpi, :carburante def initialize (colpi, carburante) @colpi = colpi @carburante = carburante end def rifornimento (quantita) @carburante += quantita end end class Camion attr_reader :posti, :carburante def initialize (posti, carburante) @posti = posti @carburante = carburante end def rifornimento (quantita) @carburante += quantita end end
Anche se scritte in modo corretto le nostre due classi non sono sicuramente scritte nel modo migliore possibile, abbiamo infatti ignorato i principi base dell'ingegneria del software. Si nota subito infatti che ci sono delle ripetizioni che possono essere eliminate utilizzando lo strumento dell'ereditarietà. Quindi un modo migliore di progettare le nostre classi prevede la creazione di una superclasse che possiede gli attributi e i metodi comuni alle sottoclassi CarroArmato
e Camion
. Ad esempio creando la classe Veicolo
nel seguente modo:
class Veicolo attr_reader :carburante def initialize (carburante) @carburante = carburante end def rifornimento (quantita) @carburante += quantita end end
possiamo scrivere le nostre classi di conseguenza:
class CarroArmato < Veicolo attr_reader :colpi def initialize (carburante, colpi) super(carburante) @colpi = colpi end end class CarroArmato < Veicolo attr_reader :colpi def initialize (carburante, colpi) super(carburante) @colpi = colpi end end
Vediamo cosa è successo. Innanzitutto ora abbiamo una gerarchia di classi che vede in cima la classe-padre, o superclasse, Veicolo
che ha due classi-figlie, o sottoclassi, Camion
e CarroArmato
che ne ereditano il metodo rifornimento e in parte il metodo initialize
. In realtà le nostre sottoclassi specializzano il metodo initialize
di Veicolo
: per la variabile d'istanza comune, @carburante
, viene chiamato il metodo initialize
della superclasse, attraverso il costrutto super
, mentre le altre variabili sono gestite direttamente dalle sottoclassi. In definitiva super
non fa altro che richiamare il metodo con lo stesso nome contenuto nella superclasse.