Nell'introduzione abbiamo più volte accennato al fatto che il Ruby è un linguaggio completamente orientato agli oggetti (Object Oriented, in breve OO). Qui verrà chiarito cosa significa questo, perché senza conoscere gli oggetti non è possibile definire alcuni concetti di base. Paradossalmente, per spiegare un concetto che ha un riscontro pratico molto forte ed è in parte frutto di anni e anni di evoluzione tecnologica, dobbiamo ricorrere alla filosofia greca antica, a Platone per la precisione. Per il filosofo il mondo in cui viviamo non è che un'ombra: ogni cosa non è altro che una copia imperfetta di un archetipo, di un modello che si trova nel "Mondo delle Idee", l'Iperuranio. Detto in maniera estremamente semplicistica, il mouse che stiamo utilizzando ora non è altro che un tentativo dell'uomo di costruire una cosa che può vedere attraverso la mente; un tentativo malriuscito però. Se da una parte queste sono supposizioni di un filosofo sulla costituzione del mondo, dall'altra, nei linguaggi di programmazione OO, si tratta di pura realtà: da una classe (modello universale) possiamo creare tante particolarizzazioni di essa (oggetti o istanze). Il paradigma OO è molto vicino al modo di pensare umano. Ad esempio possiamo dichiarare una classe/modello di un "mouse virtuale":
class Mouse def click @status = 1 puts "Hai cliccato!" end end E in seguito creare tre oggetti diversi: a = Mouse. New b = Mouse. New c = Mouse. New
Senza soffermarci sul significato delle cose scritte che verrà chiarito a breve, abbiamo creato prima di tutto un modello di mouse e poi tre particolarizzazioni di esso contenute nelle variabili a, b e c. Si tratta di "copie" che sono strutturate secondo la classe ma che hanno ognuna un comportamento autonomo. Basti pensare, nella realtà, al progetto di un mouse, per continuare col nostro esempio, e ai vari mouse prodotti a seguito di quel progetto. Fuor di metafora: il progetto è la classe e i vari mouse sono gli oggetti. In Ruby tutto è un oggetto: i numeri sono particolarizzazioni della classe "Numeric", le stringhe della classe "String", gli array (che vedremo in seguito) della classe "Array" e così via. Soffermiamoci un momento sulle classi. Ogni classe, in generale, può contenere due tipi diversi di informazioni: variabili e metodi. Il concetto di variabile l'abbiamo già visto mentre quello di metodo è nuovo: un metodo è una funzione, una parte di codice che può essere chiamata ed eseguita da vari punti senza che il programmatore debba riscrivere ogni volta quel determinato codice. Ad esempio possiamo definire il metodo somma nel seguente modo:
def somma (a, b) c = a+b return c end
La prima riga definisce il metodo (def è una parola chiave), lo chiama "somma" e definisce due variabili (a e b) i cui valori dovranno essere forniti quando questa funzione verrà chiamata. La seconda riga registra nella variabile c la somma tra le due variabili input. La terza riga "rimanda al mittente" il risultato La quarta riga (end) chiude la definizione del metodo. Possiamo adesso usare questo metodo al posto del "+":
uno = somma (1, 2) due = somma (4, 5) e = somma uno, due
Quello che viene in realtà eseguito dall'interprete è questo:
a = 1 b = 2 c = a+b uno = c a = 4 b = 5 c = a+b due = c a = uno b = due c = a+b e = c
Per questo esempio l'utilità dei metodi può non essere chiara: se ad esempio costruiamo una funzione che calcola la media tra n numeri, che richiede varie righe di codice, ecco che si inizia ad intravedere il vantaggio che questi strumenti portano al programmatore. Possiamo ora estendere il concetto di metodo a quello di classe. Nel Ruby un metodo è sempre dichiarato in una classe (al limite è dichiarato nella classe main, quella principale). Ma a cosa serve avere un metodo in una classe? Vediamo questo esempio:
class Pippo def ciao print "Ciao a tutti!" end end
Adesso, se creiamo un oggetto a partire da questa classe, su di esso potremmo usare il metodo "ciao": io = Pippo. New io. Ciao Fare invece: Pippo. Ciao
darà errore. Vedremo in seguito come far sì che questa istruzione diventi corretta. Il concetto di classe è decisamente astratto e serve uno sforzo mentale per assorbirlo. Potrebbe non essere chiaro adesso e questo è normale. In seguito lo sarà. Un passo in avanti che aiuta a capire l'utilità pratica delle classi è la distinzione dei vari tipi di variabile che è possibile usare in Ruby. Ne esistono fondamentalmente quattro tipi: locale (che abbiamo già visto), globale, d'istanza e di classe. Una variabile locale è costituita da caratteri alfanumerici e underscore (_). L'unico vincolo è che il primo carattere dell'identificatore non sia un numero. Ecco alcuni esempi:
? ciao (Corretto) ? uno2 (Corretto) ? i223432354 (Corretto) ? _2ciao (Corretto) ? 0ciao (Errore) ? ciao! (Errore)
Come abbiamo visto in precedenza, una variabile locale non assegnata e chiamata dà errore. Una variabile globale è contraddistinta dal simbolo $ che precede il nome dell'identificatore. Dopo "$" è possibile inserire caratteri alfanumerici o underscore (_) sempre a patto che il primo carattere non sia un numero. Anzi, è corretto far seguire al $ un numero ma dopo di questo non deve esserci nessun altro carattere. Ecco una lista di esempi:
? $ciao (Corretto) ? $verde_23 (Corretto) ? $_uno (Corretto) ? $12 (Corretto) ? $1c (Errore) ? $_ciao! (Errore)
A differenza delle variabili locali, una variabile globale può non essere assegnata. Richiamare, ad esempio $ciao, senza aver prima assegnato tale variabile restituirà "nil" (equivalente del null di altri linguaggi), ovvero un oggetto nullo, ma non darà errore. Una variabile d'istanza è contraddistinta dal simbolo @ seguito, come per le globali, da caratteri alfanumerici o underscore (_). Vale il solito vincolo del primo carattere;
? @uno (Corretto) ? @ciao_37 (Corretto) ? @_alfa (Corretto) ? @12 (Corretto) ? @0c (Errore) ? @ciao? (Errore)
Anche le variabili d'istanza, non dichiarate, danno nil e non errore. Per quanto riguarda infine le variabili di classe, non ci dilunghiamo in ulteriori spiegazioni. Possiamo dire che sono esattamente uguali a quelle d'istanza, con la differenza che al posto del singolo carattere "@" sono precedute da due "@":
@@ciao
Possiamo quindi vedere il diverso significato di ogni tipo di variabile. Una variabile locale vale solo nell'ambiente in cui è definita. Analizziamo questo codice:
def ciao a = 10 end a = 0 ciao p a
Per prima cosa viene definito il metodo ciao; in seguito viene assegnata la variabile a con il valore 0, viene richiamato ciao e viene stampato il valore di a (si usa p perché puts e print non avrebbero stampato un numero). L'output sarà 0. Questo perché la definizione di "a" all'interno del metodo vale solo e soltanto nel metodo stesso. Il valore di "a" nel metodo viene cancellato quando l'esecuzione di questo si conclude. Vediamo cosa succede se invece usiamo una variabile globale:
def ciao $uno = 20 end $uno = 0 ciao p $uno
In questo caso abbiamo come output "20". Una variabile globale, per definizione, mantiene il suo valore per tutta l'esecuzione del programma. Le variabili d'istanza e di classe invece sono più complesse da analizzare, ma chiariscono meglio il concetto di classe. Iniziamo con un esempio:
class Pippo def start @memoria = 0 end def plus @memoria = @memoria + 1 end def leggi return @memoria end end
La variabile d'istanza @memoria è utilizzabile solo all'interno della classe "Pippo". La sua visibilità si limita a questa (a meno che non vengano definiti metodi che permettono di ampliarla, come vedremo in seguito). Continuiamo con l'esempio:
uno = Pippo. New due = Pippo. New uno. Start due. Start uno. Plus uno. Plus due. Plus p uno. Memoria p due. Memoria
Vengono qui creati due oggetti a partire dalla classe Pippo: uno e due. L'output di questo script sarà:
? 2 ? 1
Questo perché la variabile @memoria ha un valore per "uno" e un altro valore per "due". In generale, ogni oggetto creato a partire da una determinata classe, avrà delle nuove variabili d'istanza che hanno nuovi valori validi solo per sé. Schematizzando:
uno = Pippo. New ? < Pippo: uno @memoria = nil> uno. Start ? < Pippo: uno @memoria = 0> due = Pippo. New ? < Pippo: due @memoria = nil> due. Start ? < Pippo: due @memoria = 0> uno. Plus ? < Pippo: uno @memoria = 1> uno. Plus ? < Pippo: uno @memoria = 2> due. Plus ? < Pippo: due @memoria = 1>
Possiamo vedere velocemente la differenza tra variabili di classe e d'istanza modificando la classe nell'esempio precedente:
class Pippo def start @@memoria = 0 end def plus @@memoria = @@memoria + 1 end def leggi return @@memoria end end
Mandando di nuovo in esecuzione il codice precedente con la classe "Pippo" modificata, avremo come output:
? 3 ? 3
Una variabile di classe ha validità e visibilità all'interno della classe e il suo valore è comune a tutti gli oggetti creati a partire da quella classe. La variabile "@@memoria", vista da "uno" e vista da "due", ha lo stesso valore a differenza di "@memoria" come abbiamo visto prima. Concludiamo quindi con un esempio finale:
class Mouse @@volte = 0 def sposta (x, y) @x = x @y = y @@volte = @@volte + 1 end def x return @x end def y return @y end def quante_volte_ho_spostato_un_mouse return @@volte end end uno = Mouse. New due = Mouse. New uno. Sposta (10, 20) due. Sposta (30, 40) p uno. X, due. Y p due. X, due. Y p uno. Quante_volte_ho_spostato_un_mouse p due. Quante_volte_ho_spostato_un_mouse
Output:
? 10 ? 20 ? 30 ? 40 ? 4 ? 4 Simone Scalabrino della Freank-Expo
Autore: Freank
|
News di attualità
Autocad - Lezione 2...
In alto a destra si trovano i principali...
Il Chelsea...
I tifosi napoletani comincino a tremare, il...
Marketing on-line...
Molte persone iniziano a creare giochi già...
Calcio, l'Inter...
Mancano esattamente sette giorni alla...
Castelli della...
L'edificio odierno dell'attuale dimora...
|