Ruby language

Le classi - 2                

All'interno delle classi possiamo trovare altre interessanti entità.
Ad esempio è possible definire delle costanti. Il procedimento è molto semplice ed è identico a quanto visto nel capitolo dedicato alle variabili ed ai numeri (quindi ricordatevi dell'uso della maiuscola con la quale deve inizare il nome della costante:

class Punto
  def initialize(x, y)
    @x, @y = x, y
  end

  Zero = 0
  Inf = 99999

end

L'uso all'esterno della classe è il seguente:

puts(Punto::Zero)

Ancora più interessanti sono le variabili di classe. Il loro scopo è quello di conservare informazioni rilevanti per la classe e per gli oggetti da questa derivanti. In questo senso esse sono private e disponibili per la classe e le sue istanze (anche se ci può essere qualche escamotage per un loro uso diverso, come vedremo altrove). In questo senso non sono visibili a chi usa la classe dall'esterno. A caratterizzare questa tipologia di variabili è il loro prefisso che è costituito da un coppia di &. Quindi per definire la variabile di classe x1 dovremo scrivere:

&&x1

Peraltro le variabili di classe devono essere inizializzate prima di essere usate. Vediamo un esempio pratico:

  Esempio 11.1
1
2
3
4
5
6
7
8
9
10
11
12
13
class Punto
  @@istanze = 0
  def initialize
    @@istanze += 1
  end
  def istanze
    @@istanze
  end
end

p1 = Punto.new()
p2 = Punto.new()
puts(p2.istanze)

L'output esposto alla riga 13 è il numero 2 pari al numero delle istanze della classe Punto create alle righe 11 e 12 alla cui comparsa è corrisposto un incremento della variabile di classe interna definita alla riga 2. Dalla riga 6 alla 8 è presente un getter per la variabile di classe in parola. Possiamo anche definire un setter ad esempio modificando la classe predente come segue:

class Punto
  @@istanze = 0
  def initialize
    @@istanze += 1
  end
  def istanze
    @@istanze
  end
  def Punto.istanze=(valore)
    @@istanze = valore
  end
end

Detto questo sottolineo come, personalmente, non vedo grosse necessità, nella pratica comune, per utilizzare le variabili di classe, salvo pochi casi dove magari vengono utili al posto di variabili globali. Il problema è che si tratta di un concetto piuttosto ambiguo, solo apparentemente granitico e rassicurante ma in realtà piuttosto soggetto ad essere manipolato quindi, nel complesso, non mi sembrano molto più affidabili di altre entità. In molti non sono d'accordo con questa mia (ma non solo) linea di pensiero, ma mi sento di doverlo fare presente, poi lavorate come più vi aggrada. La pratica vi aiuterà a scegliere la strada migliore

Torniamo ora parlare dei moetodi e della loro visibilità. Esistono tre keywords in Ruby che modificano le possibilità di accesso ai metodi e quindi definiscono il perimetro del loro utilizzo; tale parole chiave sono public, private e protected. Chi ha già esperienza di programmazione object oriented troverà familiari questi modificatori.
La prima parola, public, è la più semplice; esso significa che il metodo può essere invocato liberamente senza alcuna restrizione. Ogni metodo di classe è pubblico di default. Fa eccezione ovviamente initialize che è private di default. Diversi sono ovviamente i metodi esterni alle classi privati per Object ma in sostanza richiamabili dall'interno del programma in cui sono definiti, in maniera del tutto libera.
Un metodo private è interno è riservato alla implementazione della classe stessa. Può essere richiamato da un altro metodo interno ma non può essere richiamato tramite un oggetto. In breve se m1 è un metodo privato deve essere richiamato, come suggerisce elegantemente Matz nel suo testo, come m1, in stile funzionale e non come o.m1 e nemmeno nel formato self.m. Questo non vale per i public e nemmeno per i protected.
A proposito di questi ultimi, i metodi marcati come protected, essi sono un po' più sfuggenti. Possono essere invocati dagli oggetti creati a partire dalla classe in cui sono definiti e dalle sue sottoclassi (vedremo cosa sono) e dagli oggetti da queste derivati.

Vediamo un primo esempio:

  Esempio 11.2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Test
private
  def privmet
    puts("metodo privato")
  end
protected
  def protmet
    puts("metodo protetto")
  end
public
  def publmet
    puts("metodo pubblico")
  end
end

t1 = Test.new()
t1.publmet()
t1.protmet()
t1.privmet()

Questo programma funziona solo se eliminate o commentate le rgihe 18 e 19. Come si può notare, vengono create delle sezioni, all'interno delle quali definiamo i metodi che saranno sottoposti all'azione della clausola che introduce la sezione stessa; trovo questo sistema molto comodo e leggibile. Di conseguenza, il metodo privmet sarà privato, protmet sarà pubblico e publmet invece pubblico. Proprio per questo il funzionamento del programma è quello descritto. La qual cosa certifica il differente status dei 3 metodi.
Scendendo più in dettaglio, un metodo privato può essere richiamato solo con un ricevente implicito. Di fatto questo ricevente si materializza con l'istanza corrente della classe, cosa che rende possibile chiamare il metodo senza alcuna aggiunta a livello di codice. Esempio:

  Esempio 11.3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Test
private
  def privmet
    puts("metodo privato")
  end
protected
  def protmet
    puts("metodo protetto")
  end
public
  def publmet
    puts("metodo pubblico")
    privmet

  end
end

t1 = Test.new()
t1.publmet()

Il cui output è:

metodo pubblico
metodo privato

questo perchè privmet viene chiamato all'interno del metodo pubblico e il ricevente è l'istanza stessa della classe. In questo esempio si concretizza l'aspetto "privato" del metodo. Tuttavia non è possibile l'uso tramite self.privmet come avevamo già detto e come è facile comprovare, un metodo privato non è gestibile tramite nessun tipo di istanziazione quindi anche self non va bene in quanto è già implicitamente definito su di esso. Si può definire come uno strumento puramente interno alla classe.
Un metodo protetto invece è un po' più permissivo; l'esempio 11.3  funziona perfettamente se il metodo publmet (definito alla riga 11) lo riscriviamo:

 def publmet
 puts("metodo pubblico")
 protmet()
 puts self.protmet()
end

da cui risulta che possiamo anche specificare un ricevente. Ovviamente questo tipo di procedimento funziona anche con public.
Esternamente alla classe invece è possibile richiamare solo il metodo pubblico, come alla riga 18 dell'esempio 11.3, se provate col metodo privato o con quello protetto vi ritroverete con un chiaro messaggio d'errore.
Un altro punto da segnalare è quello della dichiarazione. Il sistema visto nell'esempio 11.3 prevede, come abbiamo già spiegato, la creazione di "sezioni" dedicate ai metodi pubblici, a quelli privati ed a quelli protetti. Un altro sistema prevede un sistema di etichettatura a posteriori in questo modo

class Test
def metodo 1
end
def metodo 2
end
def metodo 3
end
def metodo 4
end

public    :metodo 1
protected :metodo 4, :metodo 2
private   :metodo 2

ovvero vengono create delle aree all'interno delle quali spcifichiamo l'accessibilità dei metodi definiti nella classe il cui nome deve essere preceduto dai due punti ed eventualmente si deve far ricorso alla virgola per separare più nomi. Molto comodo anche così.