Vediamo come è implementata in Ruby la programmazione orientata agli oggetti. Scriviamo un semplice esempio supponendo di dover programmare un videogame di guerra dove tra le altre cose sono presenti dei carri armati e dei camion per il trasporto dei soldati. Rappresentiamo questi oggetti in Ruby:
class CarroArmato def initialize puts "Sono un nuovo carro armato" end end class Camion def initialize puts "Sono un nuovo camion" end end
una volta definite le classi possiamo istanziarne degli oggetti. In irb
, dopo aver creato le classi scriviamo:
> ca = CarroArmato.new Sono un nuovo carro armato => #<CarroArmato:0xb7d327fc> > c = Camion.new Sono un nuovo camion => #<Camion:0xb7d07e78>
Vediamo in ordine cosa abbiamo fatto. Innanzitutto abbiamo creato la classe usando la parola chiave class
seguita dal nome e abbiamo terminato il codice relativo alla nostra classe con la parola chiave end. Abbiamo quindi creato il metodo initialize
che viene chiamato quando istanziamo un nuovo oggetto con il metodo new
. initialize
è quello che in OOP si dice un costruttore che viene invocato ogni volta che si crea un nuovo oggetto. Alcuni linguaggi orientati agli oggetti come il C++ contemplano anche il metodo distruttore della classe che, invocato al momento del rilascio dell'istanza di un oggetto, si occupa tra le altre cose di de-allocare la memoria impegnata dall'oggetto. Nei linguaggi moderni, come Ruby, Java o C#, le attività generalmente svolte dal distruttore vengono svolte da sistemi di garbage collection, nel caso di Ruby è l'interprete a farsene carico.
Tornando al nostro esempio, initialize
è un normale metodo ed è definito dalle parole chiave def
, seguita dal nome, e dal solito end
. Essendo un esempio banale il costruttore non fa altro che dare notizia dell'avvenuta creazione dell'oggetto stampando a video una stringa utilizzando la funzione puts
(put string).
Per fare un esempio meno banale e per vedere come si passano i parametri ad un metodo miglioriamo le nostre classi. Supponiamo di voler definire al momento della creazione degli oggetti alcuni parametri caratteristici. Ad esempio se vogliamo indicare quanto carburante e quanti colpi avrà il nostro carro armato al momento della creazione ci basta scrivere:
class CarroArmato def initialize (colpi, carburante) @carburante = carburante @colpi = colpi end end
Analogamente per i camion, con la differenza che invece dei colpi ci saranno dei posti per i passeggeri:
class Camion def initialize (posti, carburante) @carburante = carburante @posti = posti end end
Come abbiamo visto nel capitolo precedente, @carburante
, @colpi
e @posti
sono delle variabili d'istanza e sono visibili da tutti i metodi della classe.
In questo modo alla creazione dei nostri nuovi oggetti vanno passati i parametri relativi al carburante, ai colpi e ai posti:
> ca = CarroArmato.new(10, 100) => #<CarroArmato:0xb7d035fc @colpi=10, @carburante=100> c = Camion.new(20, 100) => #<Camion:0xb7ce046c @posti=20, @carburante=100>
@carburante
, @colpi
e @posti
sono detti anche attributi e definiscono come la classe è vista dall'esterno. Per poter accedere, in lettura, a tali campi occorre creare dei metodi accessori del tipo:
class CarroArmato def carburante @carburante end def colpi @colpi end end
fatto ciò possiamo scrivere cose del genere:
> puts ca.carburante => 100 > puts ca.colpi => 100
Per poter accedere agli attributi anche in scrittura occorre definire anche in questo caso un apposito metodo:
class CarroArmato def carburante=(carburante) @carburante = carburante end def colpi=(colpi) @colpi = colpi end end
e quindi siamo in grado di impostare i valori degli attributi semplicemente con
> ca.colpi = 50 => 50 > puts ca.colpi => 50